home *** CD-ROM | disk | FTP | other *** search
/ Programming a Multiplayer FPS in DirectX / Programming a Multiplayer FPS in DirectX (Companion CD).iso / DirectX / dxsdk_oct2004.exe / dxsdk.exe / Samples / Managed / Common / dxmutmisc.cs < prev    next >
Encoding:
Text File  |  2004-09-27  |  104.4 KB  |  2,485 lines

  1. //--------------------------------------------------------------------------------------
  2. // File: DXMUTMisc.cs
  3. //
  4. // Shortcut and helper functions for using DX Code
  5. //
  6. // Copyright (c) Microsoft Corporation. All rights reserved.
  7. //--------------------------------------------------------------------------------------
  8. using System;
  9. using System.IO;
  10. using System.Collections;
  11. using System.Runtime.InteropServices;
  12. using Microsoft.DirectX;
  13. using Microsoft.DirectX.Direct3D;
  14.  
  15. namespace Microsoft.Samples.DirectX.UtilityToolkit
  16. {
  17.     #region Native Methods
  18.     /// <summary>
  19.     /// Will hold native methods which are interop'd
  20.     /// </summary>
  21.     public class NativeMethods
  22.     {
  23.         #region Win32 User Messages / Structures
  24.         /// <summary>Show window flags styles</summary>
  25.         public enum ShowWindowFlags: uint
  26.         {
  27.             Hide = 0,
  28.             ShowNormal = 1,
  29.             Normal = 1,
  30.             ShowMinimized = 2,
  31.             ShowMaximized = 3,
  32.             ShowNoActivate = 4,
  33.             Show = 5,
  34.             Minimize = 6,
  35.             ShowMinNoActivate = 7,
  36.             ShowNotActivated = 8,
  37.             Restore = 9,
  38.             ShowDefault = 10,
  39.             ForceMinimize = 11,
  40.  
  41.         }
  42.         /// <summary>Window styles</summary>
  43.         [Flags]
  44.         public enum WindowStyles: uint
  45.         {
  46.             Overlapped = 0x00000000,
  47.             Popup = 0x80000000,
  48.             Child = 0x40000000,
  49.             Minimize = 0x20000000,
  50.             Visible = 0x10000000,
  51.             Disabled = 0x08000000,
  52.             ClipSiblings = 0x04000000,
  53.             ClipChildren = 0x02000000,
  54.             Maximize = 0x01000000,
  55.             Caption = 0x00C00000,     /* WindowStyles.Border | WindowStyles.DialogFrame  */
  56.             Border = 0x00800000,
  57.             DialogFrame = 0x00400000,
  58.             VerticalScroll = 0x00200000,
  59.             HorizontalScroll = 0x00100000,
  60.             SystemMenu = 0x00080000,
  61.             ThickFrame = 0x00040000,
  62.             Group = 0x00020000,
  63.             TabStop = 0x00010000,
  64.             MinimizeBox = 0x00020000,
  65.             MaximizeBox = 0x00010000,
  66.     }
  67.  
  68.         /// <summary>Peek message flags</summary>
  69.         public enum PeekMessageFlags : uint
  70.         {
  71.             NoRemove = 0,
  72.             Remove = 1,
  73.             NoYield = 2,
  74.         }
  75.  
  76.         /// <summary>Window messages</summary>
  77.         public enum WindowMessage : uint
  78.         {
  79.             // Misc messages
  80.             Destroy = 0x0002,
  81.             Close = 0x0010,
  82.             Quit = 0x0012,
  83.             Paint = 0x000F,
  84.             SetCursor = 0x0020,
  85.             ActivateApplication = 0x001C,
  86.             EnterMenuLoop = 0x0211,
  87.             ExitMenuLoop = 0x0212,
  88.             NonClientHitTest = 0x0084,
  89.             PowerBroadcast = 0x0218,
  90.             SystemCommand = 0x0112,
  91.             GetMinMax = 0x0024,
  92.  
  93.             // Keyboard messages
  94.             KeyDown = 0x0100,
  95.             KeyUp = 0x0101,
  96.             Character = 0x0102,
  97.             SystemKeyDown = 0x0104,
  98.             SystemKeyUp = 0x0105,
  99.             SystemCharacter = 0x0106,
  100.  
  101.             // Mouse messages
  102.             MouseMove = 0x0200,
  103.             LeftButtonDown = 0x0201,
  104.             LeftButtonUp = 0x0202,
  105.             LeftButtonDoubleClick = 0x0203,
  106.             RightButtonDown = 0x0204,
  107.             RightButtonUp = 0x0205,
  108.             RightButtonDoubleClick = 0x0206,
  109.             MiddleButtonDown = 0x0207,
  110.             MiddleButtonUp = 0x0208,
  111.             MiddleButtonDoubleClick = 0x0209,
  112.             MouseWheel = 0x020a,
  113.             XButtonDown = 0x020B,
  114.             XButtonUp = 0x020c,
  115.             XButtonDoubleClick = 0x020d,
  116.             MouseFirst = LeftButtonDown, // Skip mouse move, it happens a lot and there is another message for that
  117.             MouseLast = XButtonDoubleClick,
  118.  
  119.             // Sizing
  120.             EnterSizeMove = 0x0231,
  121.             ExitSizeMove = 0x0232,
  122.             Size = 0x0005,
  123.  
  124.         }
  125.  
  126.         /// <summary>Mouse buttons</summary>
  127.         public enum MouseButtons
  128.         {
  129.             Left = 0x0001,
  130.             Right = 0x0002,
  131.             Middle = 0x0010,
  132.             Side1 = 0x0020,
  133.             Side2 = 0x0040,
  134.         }
  135.  
  136.         /// <summary>Windows Message</summary>
  137.         [StructLayout(LayoutKind.Sequential)]
  138.         public struct Message
  139.         {
  140.             public IntPtr hWnd;
  141.             public WindowMessage msg;
  142.             public IntPtr wParam;
  143.             public IntPtr lParam;
  144.             public uint time;
  145.             public System.Drawing.Point p;
  146.         }
  147.  
  148.         /// <summary>MinMax Info structure</summary>
  149.         [StructLayout(LayoutKind.Sequential)]
  150.         public struct MinMaxInformation
  151.         {
  152.             public System.Drawing.Point reserved;
  153.             public System.Drawing.Point MaxSize;
  154.             public System.Drawing.Point MaxPosition;
  155.             public System.Drawing.Point MinTrackSize;
  156.             public System.Drawing.Point MaxTrackSize;
  157.         }
  158.  
  159.         /// <summary>Monitor Info structure</summary>
  160.         [StructLayout(LayoutKind.Sequential)]
  161.         public struct MonitorInformation
  162.         {
  163.             public uint Size; // Size of this structure
  164.             public System.Drawing.Rectangle MonitorRectangle;
  165.             public System.Drawing.Rectangle WorkRectangle;
  166.             public uint Flags; // Possible flags
  167.         }
  168.  
  169.         /// <summary>Window class structure</summary>
  170.         [StructLayout(LayoutKind.Sequential)]
  171.         public struct WindowClass
  172.         {
  173.             public int Styles;
  174.             [MarshalAs(UnmanagedType.FunctionPtr)] public WndProcDelegate WindowsProc;
  175.             private int ExtraClassData;
  176.             private int ExtraWindowData;
  177.             public IntPtr InstanceHandle;
  178.             public IntPtr IconHandle;
  179.             public IntPtr CursorHandle;
  180.             public IntPtr backgroundBrush;
  181.             [MarshalAs(UnmanagedType.LPTStr)] public string MenuName;
  182.             [MarshalAs(UnmanagedType.LPTStr)] public string ClassName;
  183.         }
  184.         #endregion
  185.  
  186.         #region Delegates
  187.         public delegate IntPtr WndProcDelegate(IntPtr hWnd, NativeMethods.WindowMessage msg, IntPtr wParam, IntPtr lParam);
  188.         #endregion
  189.  
  190.         #region Windows API calls
  191.         [System.Security.SuppressUnmanagedCodeSecurity] // We won't use this maliciously
  192.         [System.Runtime.InteropServices.DllImport("winmm.dll")]
  193.         public static extern IntPtr timeBeginPeriod(uint period);
  194.  
  195.         [System.Security.SuppressUnmanagedCodeSecurity] // We won't use this maliciously
  196.         [DllImport("User32.dll", CharSet=CharSet.Auto)]
  197.         public static extern bool PeekMessage(out Message msg, IntPtr hWnd, uint messageFilterMin, uint messageFilterMax, PeekMessageFlags flags);
  198.         
  199.         [System.Security.SuppressUnmanagedCodeSecurity] // We won't use this maliciously
  200.         [DllImport("User32.dll", CharSet=CharSet.Auto)]
  201.         public static extern bool TranslateMessage(ref Message msg);
  202.         
  203.         [System.Security.SuppressUnmanagedCodeSecurity] // We won't use this maliciously
  204.         [DllImport("User32.dll", CharSet=CharSet.Auto)]
  205.         public static extern bool DispatchMessage(ref Message msg);
  206.         
  207.         [System.Security.SuppressUnmanagedCodeSecurity] // We won't use this maliciously
  208.         [DllImport("User32.dll", CharSet=CharSet.Auto)]
  209.         public static extern IntPtr DefWindowProc(IntPtr hWnd, WindowMessage msg, IntPtr wParam, IntPtr lParam);
  210.  
  211.         [System.Security.SuppressUnmanagedCodeSecurity] // We won't use this maliciously
  212.         [DllImport("User32.dll", CharSet=CharSet.Auto)]
  213.         public static extern void PostQuitMessage(int exitCode);
  214.  
  215.         [System.Security.SuppressUnmanagedCodeSecurity] // We won't use this maliciously
  216.         [DllImport("User32.dll", CharSet=CharSet.Auto)]
  217.         #if(_WIN64)
  218.         private static extern IntPtr SetWindowLongPtr(IntPtr hWnd, int index, [MarshalAs(UnmanagedType.FunctionPtr)] WndProcDelegate windowCallback);
  219.         #else
  220.         private static extern IntPtr SetWindowLong(IntPtr hWnd, int index, [MarshalAs(UnmanagedType.FunctionPtr)] WndProcDelegate windowCallback);
  221.         #endif
  222.  
  223.         [System.Security.SuppressUnmanagedCodeSecurity] // We won't use this maliciously
  224.         [DllImport("User32.dll", EntryPoint="SetWindowLong", CharSet=CharSet.Auto)]
  225.         private static extern IntPtr SetWindowLongStyle(IntPtr hWnd, int index, WindowStyles style);
  226.  
  227.         [System.Security.SuppressUnmanagedCodeSecurity] // We won't use this maliciously
  228.         [DllImport("User32.dll", EntryPoint="GetWindowLong", CharSet=CharSet.Auto)]
  229.         private static extern WindowStyles GetWindowLongStyle(IntPtr hWnd, int index);
  230.  
  231.         [System.Security.SuppressUnmanagedCodeSecurity] // We won't use this maliciously
  232.         [DllImport("kernel32")]
  233.         public static extern bool QueryPerformanceFrequency(ref long PerformanceFrequency);
  234.  
  235.         [System.Security.SuppressUnmanagedCodeSecurity] // We won't use this maliciously
  236.         [DllImport("kernel32")]
  237.         public static extern bool QueryPerformanceCounter(ref long PerformanceCount);
  238.  
  239.         [System.Security.SuppressUnmanagedCodeSecurity] // We won't use this maliciously
  240.         [DllImport("User32.dll", CharSet=CharSet.Auto)]
  241.         public static extern bool GetClientRect(IntPtr hWnd, out System.Drawing.Rectangle rect);
  242.  
  243.         [System.Security.SuppressUnmanagedCodeSecurity] // We won't use this maliciously
  244.         [DllImport("User32.dll", CharSet=CharSet.Auto)]
  245.         public static extern bool GetWindowRect(IntPtr hWnd, out System.Drawing.Rectangle rect);
  246.  
  247.         [System.Security.SuppressUnmanagedCodeSecurity] // We won't use this maliciously
  248.         [DllImport("User32.dll", CharSet=CharSet.Auto)]
  249.         public static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndAfter, int x, int y, int w, int h, uint flags);
  250.  
  251.         [System.Security.SuppressUnmanagedCodeSecurity] // We won't use this maliciously
  252.         [DllImport("User32.dll", CharSet=CharSet.Auto)]
  253.         public static extern bool ScreenToClient(IntPtr hWnd, ref System.Drawing.Point rect);
  254.  
  255.         [System.Security.SuppressUnmanagedCodeSecurity] // We won't use this maliciously
  256.         [DllImport("User32.dll", CharSet=CharSet.Auto)]
  257.         public static extern IntPtr SetFocus(IntPtr hWnd);
  258.  
  259.         [System.Security.SuppressUnmanagedCodeSecurity] // We won't use this maliciously
  260.         [DllImport("User32.dll", CharSet=CharSet.Auto)]
  261.         public static extern IntPtr GetParent(IntPtr hWnd);
  262.  
  263.         [System.Security.SuppressUnmanagedCodeSecurity] // We won't use this maliciously
  264.         [DllImport("User32.dll", CharSet=CharSet.Auto)]
  265.         public static extern bool GetMonitorInfo(IntPtr hWnd, ref MonitorInformation info);
  266.  
  267.         [System.Security.SuppressUnmanagedCodeSecurity] // We won't use this maliciously
  268.         [DllImport("User32.dll", CharSet=CharSet.Auto)]
  269.         public static extern IntPtr MonitorFromWindow(IntPtr hWnd, uint flags);
  270.  
  271.         [System.Security.SuppressUnmanagedCodeSecurity] // We won't use this maliciously
  272.         [DllImport("User32.dll", CharSet=CharSet.Auto)]
  273.         public static extern short GetAsyncKeyState(uint key);
  274.  
  275.         [System.Security.SuppressUnmanagedCodeSecurity] // We won't use this maliciously
  276.         [DllImport("User32.dll", CharSet=CharSet.Auto)]
  277.         public static extern IntPtr SetCapture(IntPtr handle);
  278.  
  279.         [System.Security.SuppressUnmanagedCodeSecurity] // We won't use this maliciously
  280.         [DllImport("User32.dll", CharSet=CharSet.Auto)]
  281.         public static extern bool ReleaseCapture();
  282.  
  283.         [System.Security.SuppressUnmanagedCodeSecurity] // We won't use this maliciously
  284.         [DllImport("User32.dll", CharSet=CharSet.Auto)]
  285.         public static extern bool ShowWindow(IntPtr hWnd, ShowWindowFlags flags);
  286.  
  287.         [System.Security.SuppressUnmanagedCodeSecurity] // We won't use this maliciously
  288.         [DllImport("User32.dll", CharSet=CharSet.Auto)]
  289.         public static extern bool SetMenu(IntPtr hWnd, IntPtr menuHandle);
  290.  
  291.         [System.Security.SuppressUnmanagedCodeSecurity] // We won't use this maliciously
  292.         [DllImport("User32.dll", CharSet=CharSet.Auto)]
  293.         public static extern bool DestroyWindow(IntPtr hWnd);
  294.  
  295.         [System.Security.SuppressUnmanagedCodeSecurity] // We won't use this maliciously
  296.         [DllImport("User32.dll", CharSet=CharSet.Auto)]
  297.         public static extern bool IsIconic(IntPtr hWnd);
  298.  
  299.         [System.Security.SuppressUnmanagedCodeSecurity] // We won't use this maliciously
  300.         [DllImport("User32.dll", CharSet=CharSet.Auto)]
  301.         public static extern bool AdjustWindowRect(ref System.Drawing.Rectangle rect, WindowStyles style, 
  302.             [MarshalAs(UnmanagedType.Bool)]bool menu);
  303.  
  304.         [System.Security.SuppressUnmanagedCodeSecurity] // We won't use this maliciously
  305.         [DllImport("User32.dll", CharSet=CharSet.Auto)]
  306.         public static extern IntPtr SendMessage(IntPtr windowHandle, WindowMessage msg, IntPtr w, IntPtr l);
  307.  
  308.         [System.Security.SuppressUnmanagedCodeSecurity] // We won't use this maliciously
  309.         [DllImport("User32.dll", CharSet=CharSet.Auto)]
  310.         public static extern IntPtr RegisterClass(ref WindowClass wndClass);
  311.  
  312.         [System.Security.SuppressUnmanagedCodeSecurity] // We won't use this maliciously
  313.         [DllImport("User32.dll", CharSet=CharSet.Auto)]
  314.         public static extern bool UnregisterClass([MarshalAs(UnmanagedType.LPTStr)] string className, IntPtr instanceHandle);
  315.  
  316.         [System.Security.SuppressUnmanagedCodeSecurity] // We won't use this maliciously
  317.         [DllImport("User32.dll", EntryPoint="CreateWindowEx", CharSet=CharSet.Auto)]
  318.         public static extern IntPtr CreateWindow(int exStyle, [MarshalAs(UnmanagedType.LPTStr)] string className, [MarshalAs(UnmanagedType.LPTStr)] string windowName,
  319.             WindowStyles style, int x, int y, int width, int height, IntPtr parent, IntPtr menuHandle, IntPtr instanceHandle, IntPtr zero);
  320.  
  321.         [System.Security.SuppressUnmanagedCodeSecurity] // We won't use this maliciously
  322.         [DllImport("User32.dll", CharSet=CharSet.Auto)]
  323.         public static extern int GetCaretBlinkTime();
  324.         #endregion
  325.  
  326.         #region Class Methods
  327.         private NativeMethods() {} // No creation
  328.         /// <summary>Hooks window messages to go through this new callback</summary>
  329.         public static void HookWindowsMessages(IntPtr window, WndProcDelegate callback)
  330.         {
  331.             #if(_WIN64)
  332.                 SetWindowLongPtr(window, -4, callback);
  333.             #else
  334.                 SetWindowLong(window, -4, callback);
  335.             #endif
  336.         }
  337.         /// <summary>Set new window style</summary>
  338.         public static void SetStyle(IntPtr window, WindowStyles newStyle)
  339.         {
  340.             SetWindowLongStyle(window, -16, newStyle);
  341.         }
  342.         /// <summary>Get new window style</summary>
  343.         public static WindowStyles GetStyle(IntPtr window)
  344.         {
  345.             return GetWindowLongStyle(window, -16);
  346.         }
  347.  
  348.         /// <summary>Returns the low word</summary>
  349.         public static short LoWord(uint l)
  350.         {
  351.             return (short)(l & 0xffff);
  352.         }
  353.         /// <summary>Returns the high word</summary>
  354.         public static short HiWord(uint l)
  355.         {
  356.             return (short)(l >> 16);
  357.         }
  358.  
  359.         /// <summary>Makes two shorts into a long</summary>
  360.         public static uint MakeUInt32(short l, short r)
  361.         {
  362.             return (uint)((l & 0xffff) | ((r & 0xffff) << 16));
  363.         }
  364.  
  365.         /// <summary>Is this key down right now</summary>
  366.         public static bool IsKeyDown(System.Windows.Forms.Keys key)
  367.         {
  368.             return (GetAsyncKeyState((int)System.Windows.Forms.Keys.ShiftKey) & 0x8000) != 0;
  369.         }
  370.         #endregion
  371.     }
  372.  
  373.     #endregion
  374.  
  375.     #region Timer
  376.     public class FrameworkTimer
  377.     {
  378.         #region Instance Data
  379.         private static bool isUsingQPF;
  380.         private static bool isTimerStopped;
  381.         private static long ticksPerSecond;
  382.         private static long stopTime;
  383.         private static long lastElapsedTime;
  384.         private static long baseTime;
  385.         #endregion
  386.  
  387.         #region Creation
  388.         private FrameworkTimer() { } // No creation
  389.         /// <summary>
  390.         /// Static creation routine
  391.         /// </summary>
  392.         static FrameworkTimer()
  393.         {
  394.             isTimerStopped = true;
  395.             ticksPerSecond = 0;
  396.             stopTime = 0;
  397.             lastElapsedTime = 0;
  398.             baseTime = 0;
  399.             // Use QueryPerformanceFrequency to get frequency of the timer
  400.             isUsingQPF = NativeMethods.QueryPerformanceFrequency(ref ticksPerSecond);
  401.         }
  402.         #endregion
  403.  
  404.         /// <summary>
  405.         /// Resets the timer
  406.         /// </summary>
  407.         public static void Reset()
  408.         {
  409.             if (!isUsingQPF)
  410.                 return; // Nothing to do
  411.  
  412.             // Get either the current time or the stop time
  413.             long time = 0;
  414.             if (stopTime != 0)
  415.                 time = stopTime;
  416.             else
  417.                 NativeMethods.QueryPerformanceCounter(ref time);
  418.  
  419.             baseTime = time;
  420.             lastElapsedTime = time;
  421.             stopTime = 0;
  422.             isTimerStopped = false;
  423.         }
  424.  
  425.         /// <summary>
  426.         /// Starts the timer
  427.         /// </summary>
  428.         public static void Start()
  429.         {
  430.             if (!isUsingQPF)
  431.                 return; // Nothing to do
  432.  
  433.             // Get either the current time or the stop time
  434.             long time = 0;
  435.             if (stopTime != 0)
  436.                 time = stopTime;
  437.             else
  438.                 NativeMethods.QueryPerformanceCounter(ref time);
  439.  
  440.             if (isTimerStopped)
  441.                 baseTime += (time - stopTime);
  442.             stopTime = 0;
  443.             lastElapsedTime = time;
  444.             isTimerStopped = false;
  445.         }
  446.  
  447.         /// <summary>
  448.         /// Stop (or pause) the timer
  449.         /// </summary>
  450.         public static void Stop()
  451.         {
  452.             if (!isUsingQPF)
  453.                 return; // Nothing to do
  454.  
  455.             if (!isTimerStopped)
  456.             {
  457.                 // Get either the current time or the stop time
  458.                 long time = 0;
  459.                 if (stopTime != 0)
  460.                     time = stopTime;
  461.                 else
  462.                     NativeMethods.QueryPerformanceCounter(ref time);
  463.  
  464.                 stopTime = time;
  465.                 lastElapsedTime = time;
  466.                 isTimerStopped = true;
  467.             }
  468.         }
  469.  
  470.         /// <summary>
  471.         /// Advance the timer a tenth of a second
  472.         /// </summary>
  473.         public static void Advance()
  474.         {
  475.             if (!isUsingQPF)
  476.                 return; // Nothing to do
  477.  
  478.             stopTime += ticksPerSecond / 10;
  479.         }
  480.  
  481.         /// <summary>
  482.         /// Get the absolute system time
  483.         /// </summary>
  484.         public static double GetAbsoluteTime()
  485.         {
  486.             if (!isUsingQPF)
  487.                 return -1.0; // Nothing to do
  488.  
  489.             // Get either the current time or the stop time
  490.             long time = 0;
  491.             if (stopTime != 0)
  492.                 time = stopTime;
  493.             else
  494.                 NativeMethods.QueryPerformanceCounter(ref time);
  495.  
  496.             double absolueTime = time / (double)ticksPerSecond;
  497.             return absolueTime;
  498.         }
  499.  
  500.         /// <summary>
  501.         /// Get the current time
  502.         /// </summary>
  503.         public static double GetTime()
  504.         {
  505.             if (!isUsingQPF)
  506.                 return -1.0; // Nothing to do
  507.  
  508.             // Get either the current time or the stop time
  509.             long time = 0;
  510.             if (stopTime != 0)
  511.                 time = stopTime;
  512.             else
  513.                 NativeMethods.QueryPerformanceCounter(ref time);
  514.  
  515.             double appTime = (double)(time - baseTime) / (double)ticksPerSecond;
  516.             return appTime;
  517.         }
  518.  
  519.         /// <summary>
  520.         /// get the time that elapsed between GetElapsedTime() calls
  521.         /// </summary>
  522.         public static double GetElapsedTime()
  523.         {
  524.             if (!isUsingQPF)
  525.                 return -1.0; // Nothing to do
  526.  
  527.             // Get either the current time or the stop time
  528.             long time = 0;
  529.             if (stopTime != 0)
  530.                 time = stopTime;
  531.             else
  532.                 NativeMethods.QueryPerformanceCounter(ref time);
  533.  
  534.             double elapsedTime = (double)(time - lastElapsedTime) / (double)ticksPerSecond;
  535.             lastElapsedTime = time;
  536.             return elapsedTime;
  537.         }
  538.  
  539.         /// <summary>
  540.         /// Returns true if timer stopped
  541.         /// </summary>
  542.         public static bool IsStopped
  543.         {
  544.             get { return isTimerStopped; }
  545.         }
  546.     }
  547.     #endregion
  548.  
  549.     #region Resource Cache
  550.     /// <summary>Information about a cached texture</summary>
  551.     struct CachedTexture
  552.     {
  553.         public string Source; // Data source
  554.         public int Width;
  555.         public int Height;
  556.         public int Depth;
  557.         public int MipLevels;
  558.         public Usage Usage;
  559.         public Format Format;
  560.         public Pool Pool;
  561.         public ResourceType Type;
  562.     }
  563.  
  564.     /// <summary>Information about a cached effect</summary>
  565.     struct CachedEffect
  566.     {
  567.         public string Source; // Data source
  568.         public ShaderFlags Flags;
  569.     }
  570.  
  571.     /// <summary>
  572.     /// Will be a resource cache for any resources that may be required by a sample
  573.     /// This class will be 'static'
  574.     /// </summary>
  575.     public class ResourceCache
  576.     {
  577.         #region Creation
  578.         private ResourceCache() { } // Don't allow creation
  579.         private static ResourceCache localObject = null;
  580.         public static ResourceCache GetGlobalInstance()
  581.         {
  582.             if (localObject == null)
  583.                 localObject = new ResourceCache();
  584.  
  585.             return localObject;
  586.         }
  587.         #endregion
  588.  
  589.         protected Hashtable textureCache = new Hashtable(); // Cache of textures
  590.         protected Hashtable effectCache = new Hashtable(); // Cache of effects
  591.         protected Hashtable fontCache = new Hashtable(); // Cache of fonts
  592.  
  593.         #region Cache Creation Methods
  594.  
  595.         /// <summary>Create a texture from a file</summary>
  596.         public Texture CreateTextureFromFile(Device device, string filename)
  597.         {
  598.             return CreateTextureFromFileEx(device, filename, D3DX.Default, D3DX.Default, D3DX.Default, Usage.None,
  599.                 Format.Unknown, Pool.Managed, (Filter)D3DX.Default, (Filter)D3DX.Default, 0);
  600.         }
  601.         /// <summary>Create a texture from a file</summary>
  602.         public Texture CreateTextureFromFileEx(Device device, string filename, int w, int h, int mip, Usage usage, Format fmt, Pool pool, Filter filter, Filter mipfilter, int colorkey)
  603.         {
  604.             // Search the cache first
  605.             foreach(CachedTexture ct in textureCache.Keys)
  606.             {
  607.                 if ( (string.Compare(ct.Source, filename, true) == 0) &&
  608.                     ct.Width == w &&
  609.                     ct.Height == h &&
  610.                     ct.MipLevels == mip &&
  611.                     ct.Usage == usage &&
  612.                     ct.Format == fmt &&
  613.                     ct.Pool == pool &&
  614.                     ct.Type == ResourceType.Textures)
  615.                 {
  616.                     // A match was found, return that
  617.                     return textureCache[ct] as Texture;
  618.                 }
  619.             }
  620.  
  621.             // No matching entry, load the resource and add it to the cache
  622.             Texture t = TextureLoader.FromFile(device, filename, w, h, mip, usage, fmt, pool, filter, mipfilter, colorkey);
  623.             CachedTexture entry = new CachedTexture();
  624.             entry.Source = filename;
  625.             entry.Width = w;
  626.             entry.Height = h;
  627.             entry.MipLevels = mip;
  628.             entry.Usage = usage;
  629.             entry.Format = fmt;
  630.             entry.Pool = pool;
  631.             entry.Type = ResourceType.Textures;
  632.  
  633.             textureCache.Add(entry, t);
  634.  
  635.             return t;
  636.         }
  637.         /// <summary>Create a cube texture from a file</summary>
  638.         public CubeTexture CreateCubeTextureFromFile(Device device, string filename)
  639.         {
  640.             return CreateCubeTextureFromFileEx(device, filename, D3DX.Default, D3DX.Default, Usage.None,
  641.                 Format.Unknown, Pool.Managed, (Filter)D3DX.Default, (Filter)D3DX.Default, 0);
  642.         }
  643.         /// <summary>Create a cube texture from a file</summary>
  644.         public CubeTexture CreateCubeTextureFromFileEx(Device device, string filename, int size, int mip, Usage usage, Format fmt, Pool pool, Filter filter, Filter mipfilter, int colorkey)
  645.         {
  646.             // Search the cache first
  647.             foreach(CachedTexture ct in textureCache.Keys)
  648.             {
  649.                 if ( (string.Compare(ct.Source, filename, true) == 0) &&
  650.                     ct.Width == size &&
  651.                     ct.MipLevels == mip &&
  652.                     ct.Usage == usage &&
  653.                     ct.Format == fmt &&
  654.                     ct.Pool == pool &&
  655.                     ct.Type == ResourceType.CubeTexture)
  656.                 {
  657.                     // A match was found, return that
  658.                     return textureCache[ct] as CubeTexture;
  659.                 }
  660.             }
  661.  
  662.             // No matching entry, load the resource and add it to the cache
  663.             CubeTexture t = TextureLoader.FromCubeFile(device, filename, size, mip, usage, fmt, pool, filter, mipfilter, colorkey);
  664.             CachedTexture entry = new CachedTexture();
  665.             entry.Source = filename;
  666.             entry.Width = size;
  667.             entry.MipLevels = mip;
  668.             entry.Usage = usage;
  669.             entry.Format = fmt;
  670.             entry.Pool = pool;
  671.             entry.Type = ResourceType.CubeTexture;
  672.  
  673.             textureCache.Add(entry, t);
  674.  
  675.             return t;
  676.         }
  677.         /// <summary>Create a volume texture from a file</summary>
  678.         public VolumeTexture CreateVolumeTextureFromFile(Device device, string filename)
  679.         {
  680.             return CreateVolumeTextureFromFileEx(device, filename, D3DX.Default, D3DX.Default, D3DX.Default, D3DX.Default, Usage.None,
  681.                 Format.Unknown, Pool.Managed, (Filter)D3DX.Default, (Filter)D3DX.Default, 0);
  682.         }
  683.         /// <summary>Create a volume texture from a file</summary>
  684.         public VolumeTexture CreateVolumeTextureFromFileEx(Device device, string filename, int w, int h, int d, int mip, Usage usage, Format fmt, Pool pool, Filter filter, Filter mipfilter, int colorkey)
  685.         {
  686.             // Search the cache first
  687.             foreach(CachedTexture ct in textureCache.Keys)
  688.             {
  689.                 if ( (string.Compare(ct.Source, filename, true) == 0) &&
  690.                     ct.Width == w &&
  691.                     ct.Height == h &&
  692.                     ct.Depth == d &&
  693.                     ct.MipLevels == mip &&
  694.                     ct.Usage == usage &&
  695.                     ct.Format == fmt &&
  696.                     ct.Pool == pool &&
  697.                     ct.Type == ResourceType.VolumeTexture)
  698.                 {
  699.                     // A match was found, return that
  700.                     return textureCache[ct] as VolumeTexture;
  701.                 }
  702.             }
  703.  
  704.             // No matching entry, load the resource and add it to the cache
  705.             VolumeTexture t = TextureLoader.FromVolumeFile(device, filename, w, h, d, mip, usage, fmt, pool, filter, mipfilter, colorkey);
  706.             CachedTexture entry = new CachedTexture();
  707.             entry.Source = filename;
  708.             entry.Width = w;
  709.             entry.Height = h;
  710.             entry.Depth = d;
  711.             entry.MipLevels = mip;
  712.             entry.Usage = usage;
  713.             entry.Format = fmt;
  714.             entry.Pool = pool;
  715.             entry.Type = ResourceType.VolumeTexture;
  716.  
  717.             textureCache.Add(entry, t);
  718.  
  719.             return t;
  720.         }
  721.  
  722.         /// <summary>Create an effect from a file</summary>
  723.         public Effect CreateEffectFromFile(Device device, string filename, Macro[] defines, Include includeFile, ShaderFlags flags, EffectPool effectPool, out string errors)
  724.         {
  725.             // No errors at first!
  726.             errors = string.Empty;
  727.             // Search the cache first
  728.             foreach(CachedEffect ce in effectCache.Keys)
  729.             {
  730.                 if ( (string.Compare(ce.Source, filename, true) == 0) &&
  731.                     ce.Flags == flags)
  732.                 {
  733.                     // A match was found, return that
  734.                     return effectCache[ce] as Effect;
  735.                 }
  736.             }
  737.  
  738.             // Nothing found in the cache
  739.             Effect e = Effect.FromFile(device, filename, defines, includeFile, flags, effectPool, out errors);
  740.             // Add this to the cache
  741.             CachedEffect entry = new CachedEffect();
  742.             entry.Flags = flags;
  743.             entry.Source = filename;
  744.             effectCache.Add(entry, e);
  745.  
  746.             // Return the new effect
  747.             return e;
  748.         }
  749.  
  750.         /// <summary>Create an effect from a file</summary>
  751.         public Effect CreateEffectFromFile(Device device, string filename, Macro[] defines, Include includeFile, ShaderFlags flags, EffectPool effectPool)
  752.         { 
  753.             string temp; return CreateEffectFromFile(device, filename, defines, includeFile, flags, effectPool, out temp);
  754.         }
  755.         /// <summary>Create a font object</summary>
  756.         public Font CreateFont(Device device, int height, int width, FontWeight weight, int mip, bool italic,
  757.             CharacterSet charSet, Precision outputPrecision, FontQuality quality, PitchAndFamily pandf, string fontName)
  758.         {
  759.             // Create the font description
  760.             FontDescription desc = new FontDescription();
  761.             desc.Height = height;
  762.             desc.Width = width;
  763.             desc.Weight = weight;
  764.             desc.MipLevels = mip;
  765.             desc.IsItalic = italic;
  766.             desc.CharSet = charSet;
  767.             desc.OutputPrecision = outputPrecision;
  768.             desc.Quality = quality;
  769.             desc.PitchAndFamily = pandf;
  770.             desc.FaceName = fontName;
  771.  
  772.             // return the font
  773.             return CreateFont(device, desc);
  774.         }
  775.         /// <summary>Create a font object</summary>
  776.         public Font CreateFont(Device device, FontDescription desc)
  777.         {
  778.             // Search the cache first
  779.             foreach(FontDescription fd in fontCache.Keys)
  780.             {
  781.                 if ( (string.Compare(fd.FaceName, desc.FaceName, true) == 0) &&
  782.                     fd.CharSet == desc.CharSet &&
  783.                     fd.Height == desc.Height &&
  784.                     fd.IsItalic == desc.IsItalic &&
  785.                     fd.MipLevels == desc.MipLevels &&
  786.                     fd.OutputPrecision == desc.OutputPrecision &&
  787.                     fd.PitchAndFamily == desc.PitchAndFamily &&
  788.                     fd.Quality == desc.Quality &&
  789.                     fd.Weight == desc.Weight &&
  790.                     fd.Width == desc.Width)
  791.                 {
  792.                     // A match was found, return that
  793.                     return fontCache[fd] as Font;
  794.                 }
  795.             }
  796.  
  797.             // Couldn't find anything in the cache, create one
  798.             Font f = new Font(device, desc);
  799.             // Create a new entry
  800.             fontCache.Add(desc, f);
  801.  
  802.             // return the new font
  803.             return f;
  804.         }
  805.  
  806.         #endregion
  807.  
  808.         #region Device event callbacks
  809.         /// <summary>
  810.         /// Called when the device is created
  811.         /// </summary>
  812.         public void OnCreateDevice(Device device) {} // Nothing to do on device create
  813.         /// <summary>
  814.         /// Called when the device is reset
  815.         /// </summary>
  816.         public void OnResetDevice(Device device)
  817.         {
  818.             // Call OnResetDevice on all effect and font objects
  819.             foreach(Font f in fontCache.Values)
  820.                 f.OnResetDevice();
  821.             foreach(Effect e in effectCache.Values)
  822.                 e.OnResetDevice();
  823.         }
  824.         /// <summary>
  825.         /// Clear any resources that need to be lost
  826.         /// </summary>
  827.         public void OnLostDevice()
  828.         {
  829.             foreach(Font f in fontCache.Values)
  830.                 f.OnLostDevice();
  831.             foreach(Effect e in effectCache.Values)
  832.                 e.OnLostDevice();
  833.  
  834.             // Search the texture cache 
  835.             foreach(CachedTexture ct in textureCache.Keys)
  836.             {
  837.                 if (ct.Pool == Pool.Default)
  838.                 {
  839.                     // A match was found, get rid of it
  840.                     switch(ct.Type)
  841.                     {
  842.                         case ResourceType.Textures:
  843.                             (textureCache[ct] as Texture).Dispose(); break;
  844.                         case ResourceType.CubeTexture:
  845.                             (textureCache[ct] as CubeTexture).Dispose();break;
  846.                         case ResourceType.VolumeTexture:
  847.                             (textureCache[ct] as VolumeTexture).Dispose();break;
  848.                     }
  849.                 }
  850.             }
  851.         }
  852.         /// <summary>
  853.         /// Destroy any resources and clear the caches
  854.         /// </summary>
  855.         public void OnDestroyDevice()
  856.         {
  857.             // Cleanup the fonts
  858.             foreach(Font f in fontCache.Values)
  859.                 f.Dispose();
  860.  
  861.             // Cleanup the effects
  862.             foreach(Effect e in effectCache.Values)
  863.                 e.Dispose();
  864.  
  865.             // Dispose of any items in the caches
  866.             foreach(BaseTexture texture in textureCache.Values)
  867.             {
  868.                 if (texture != null)
  869.                     texture.Dispose();
  870.             }
  871.  
  872.             // Clear all of the caches
  873.             textureCache.Clear();
  874.             fontCache.Clear();
  875.             effectCache.Clear();
  876.         }
  877.  
  878.         #endregion
  879.     }
  880.     #endregion
  881.  
  882.     #region Arcball
  883.     /// <summary>
  884.     /// Class holds arcball data
  885.     /// </summary>
  886.     public class ArcBall
  887.     {
  888.         #region Instance Data
  889.         protected Matrix rotation; // Matrix for arc ball's orientation
  890.         protected Matrix translation; // Matrix for arc ball's position
  891.         protected Matrix translationDelta; // Matrix for arc ball's position
  892.  
  893.         protected int width; // arc ball's window width
  894.         protected int height; // arc ball's window height
  895.         protected Vector2 center;  // center of arc ball 
  896.         protected float radius; // arc ball's radius in screen coords
  897.         protected float radiusTranslation; // arc ball's radius for translating the target
  898.  
  899.         protected Quaternion downQuat; // Quaternion before button down
  900.         protected Quaternion nowQuat; // Composite quaternion for current drag
  901.         protected bool isDragging; // Whether user is dragging arc ball
  902.  
  903.         protected System.Drawing.Point lastMousePosition; // position of last mouse point
  904.         protected Vector3 downPt; // starting point of rotation arc
  905.         protected Vector3 currentPt; // current point of rotation arc
  906.         #endregion
  907.  
  908.         #region Simple Properties
  909.         /// <summary>Gets the rotation matrix</summary>
  910.         public Matrix RotationMatrix { get { return rotation = Matrix.RotationQuaternion(nowQuat); } }
  911.         /// <summary>Gets the translation matrix</summary>
  912.         public Matrix TranslationMatrix { get { return translation; } }
  913.         /// <summary>Gets the translation delta matrix</summary>
  914.         public Matrix TranslationDeltaMatrix { get { return translationDelta; } }
  915.         /// <summary>Gets the dragging state</summary>
  916.         public bool IsBeingDragged { get { return isDragging; } }
  917.         /// <summary>Gets or sets the current quaternion</summary>
  918.         public Quaternion CurrentQuaternion { get { return nowQuat; } set { nowQuat = value; } }
  919.         #endregion
  920.  
  921.         // Class methods
  922.  
  923.         /// <summary>
  924.         /// Create new instance of the arcball class
  925.         /// </summary>
  926.         public ArcBall()
  927.         {
  928.             Reset();
  929.             downPt = Vector3.Empty;
  930.             currentPt = Vector3.Empty;
  931.  
  932.             System.Windows.Forms.Form active = System.Windows.Forms.Form.ActiveForm;
  933.             if (active != null)
  934.             {
  935.                 System.Drawing.Rectangle rect = active.ClientRectangle;
  936.                 SetWindow(rect.Width, rect.Height);
  937.             }
  938.         }
  939.  
  940.         /// <summary>
  941.         /// Resets the arcball
  942.         /// </summary>
  943.         public void Reset()
  944.         {
  945.             downQuat = Quaternion.Identity;
  946.             nowQuat = Quaternion.Identity;
  947.             rotation = Matrix.Identity;
  948.             translation = Matrix.Identity;
  949.             translationDelta = Matrix.Identity;
  950.             isDragging = false;
  951.             radius = 1.0f;
  952.             radiusTranslation = 1.0f;
  953.         }
  954.  
  955.         /// <summary>
  956.         /// Convert a screen point to a vector
  957.         /// </summary>
  958.         public Vector3 ScreenToVector(float screenPointX, float screenPointY)
  959.         {
  960.             float x = -(screenPointX - width / 2.0f) / (radius * width/2.0f);
  961.             float y = (screenPointY - height / 2.0f) / (radius * height/2.0f);
  962.             float z = 0.0f;
  963.             float mag = (x*x) + (y*y);
  964.  
  965.             if (mag > 1.0f)
  966.             {
  967.                 float scale = 1.0f / (float)Math.Sqrt(mag);
  968.                 x *= scale;
  969.                 y *= scale;
  970.             }
  971.             else
  972.                 z = (float)Math.Sqrt(1.0f - mag);
  973.  
  974.             return new Vector3(x,y,z);
  975.         }
  976.  
  977.         /// <summary>
  978.         /// Set window paramters
  979.         /// </summary>
  980.         public void SetWindow(int w, int h, float r)
  981.         {
  982.             width = w; height = h; radius = r;
  983.             center = new Vector2(w / 2.0f, h / 2.0f);
  984.         }
  985.         public void SetWindow(int w, int h)
  986.         {
  987.             SetWindow(w,h,0.9f); // default radius
  988.         }
  989.  
  990.         /// <summary>
  991.         /// Computes a quaternion from ball points
  992.         /// </summary>
  993.         public static Quaternion QuaternionFromBallPoints(Vector3 from, Vector3 to)
  994.         {
  995.             float dot = Vector3.Dot(from, to);
  996.             Vector3 part = Vector3.Cross(from, to);
  997.             return new Quaternion(part.X, part.Y, part.Z, dot);
  998.         }
  999.  
  1000.         /// <summary>
  1001.         /// Begin the arcball 'dragging'
  1002.         /// </summary>
  1003.         public void OnBegin(int x, int y)
  1004.         {
  1005.             isDragging = true;
  1006.             downQuat = nowQuat;
  1007.             downPt = ScreenToVector((float)x, (float)y);
  1008.         }
  1009.         /// <summary>
  1010.         /// The arcball is 'moving'
  1011.         /// </summary>
  1012.         public void OnMove(int x, int y)
  1013.         {
  1014.             if (isDragging)
  1015.             {
  1016.                 currentPt = ScreenToVector((float)x, (float)y);
  1017.                 nowQuat = downQuat * QuaternionFromBallPoints(downPt, currentPt);
  1018.             }
  1019.         }
  1020.         /// <summary>
  1021.         /// Done dragging the arcball
  1022.         /// </summary>
  1023.         public void OnEnd()
  1024.         {
  1025.             isDragging = false;
  1026.         }
  1027.  
  1028.         /// <summary>
  1029.         /// Handle messages from the window
  1030.         /// </summary>
  1031.         public bool HandleMessages(IntPtr hWnd, NativeMethods.WindowMessage msg, IntPtr wParam, IntPtr lParam)
  1032.         {
  1033.             // Current mouse position
  1034.             short mouseX = NativeMethods.LoWord((uint)lParam.ToInt32());
  1035.             short mouseY = NativeMethods.HiWord((uint)lParam.ToInt32());
  1036.  
  1037.             switch(msg)
  1038.             {
  1039.                 case NativeMethods.WindowMessage.LeftButtonDown:
  1040.                 case NativeMethods.WindowMessage.LeftButtonDoubleClick:
  1041.                     // Set capture
  1042.                     NativeMethods.SetCapture(hWnd);
  1043.                     OnBegin(mouseX, mouseY);
  1044.                     return true;
  1045.                 case NativeMethods.WindowMessage.LeftButtonUp:
  1046.                     // Release capture
  1047.                     NativeMethods.ReleaseCapture();
  1048.                     OnEnd();
  1049.                     return true;
  1050.  
  1051.                 case NativeMethods.WindowMessage.RightButtonDown:
  1052.                 case NativeMethods.WindowMessage.RightButtonDoubleClick:
  1053.                 case NativeMethods.WindowMessage.MiddleButtonDown:
  1054.                 case NativeMethods.WindowMessage.MiddleButtonDoubleClick:
  1055.                     // Set capture
  1056.                     NativeMethods.SetCapture(hWnd);
  1057.                     // Store off the position of the cursor
  1058.                     lastMousePosition = new System.Drawing.Point(mouseX, mouseY);
  1059.                     return true;
  1060.  
  1061.                 case NativeMethods.WindowMessage.RightButtonUp:
  1062.                 case NativeMethods.WindowMessage.MiddleButtonUp:
  1063.                     // Release capture
  1064.                     NativeMethods.ReleaseCapture();
  1065.                     return true;
  1066.  
  1067.                 case NativeMethods.WindowMessage.MouseMove:
  1068.                     short buttonState = NativeMethods.LoWord((uint)wParam.ToInt32());
  1069.                     bool leftButton = ((buttonState & (short)NativeMethods.MouseButtons.Left) != 0);
  1070.                     bool rightButton = ((buttonState & (short)NativeMethods.MouseButtons.Right) != 0);
  1071.                     bool middleButton = ((buttonState & (short)NativeMethods.MouseButtons.Middle) != 0);
  1072.  
  1073.                     if (leftButton)
  1074.                     {
  1075.                         OnMove(mouseX, mouseY);
  1076.                     }
  1077.                     else if (rightButton || middleButton)
  1078.                     {
  1079.                         // Normalize based on size of window and bounding sphere radius
  1080.                         float deltaX = (lastMousePosition.X - mouseX) * radiusTranslation / width;
  1081.                         float deltaY = (lastMousePosition.Y - mouseY) * radiusTranslation / height;
  1082.  
  1083.                         if (rightButton)
  1084.                         {
  1085.                             translationDelta = Matrix.Translation(-2*deltaX,2*deltaY, 0.0f);
  1086.                             translation *= translationDelta;
  1087.                         }
  1088.                         else // Middle button
  1089.                         {
  1090.                             translationDelta = Matrix.Translation(0.0f, 0.0f, 5*deltaY);
  1091.                             translation *= translationDelta;
  1092.                         }
  1093.                         // Store off the position of the cursor
  1094.                         lastMousePosition = new System.Drawing.Point(mouseX, mouseY);
  1095.                     }
  1096.                     return true;
  1097.             }
  1098.  
  1099.             return false;
  1100.         }
  1101.     }
  1102.     #endregion
  1103.  
  1104.     #region Cameras
  1105.     /// <summary>
  1106.     /// Used to map keys to the camera
  1107.     /// </summary>
  1108.     public enum CameraKeys : byte
  1109.     {
  1110.         StrafeLeft,
  1111.         StrafeRight,
  1112.         MoveForward,
  1113.         MoveBackward,
  1114.         MoveUp,
  1115.         MoveDown,
  1116.         Reset,
  1117.         ControlDown,
  1118.         MaxKeys,
  1119.         Unknown=0xff
  1120.     }
  1121.  
  1122.     /// <summary>
  1123.     /// Mouse button mask values
  1124.     /// </summary>
  1125.     [Flags]
  1126.     public enum MouseButtonMask : byte
  1127.     {
  1128.         None = 0,
  1129.         Left = 0x01,
  1130.         Middle = 0x02,
  1131.         Right = 0x04,
  1132.         Wheel = 0x08,
  1133.     }
  1134.  
  1135.     /// <summary>
  1136.     /// Simple base camera class that moves and rotates.  The base class
  1137.     /// records mouse and keyboard input for use by a derived class, and 
  1138.     /// keeps common state.
  1139.     /// </summary>
  1140.     public abstract class Camera
  1141.     {
  1142.         /// <summary>
  1143.         /// Maps NativeMethods.WindowMessage.Key* msg to a camera key
  1144.         /// </summary>
  1145.         protected static CameraKeys MapKey(IntPtr param)
  1146.         {
  1147.             System.Windows.Forms.Keys key = (System.Windows.Forms.Keys)param.ToInt32();
  1148.             switch(key)
  1149.             {
  1150.                 case System.Windows.Forms.Keys.ControlKey: return CameraKeys.ControlDown;
  1151.                 case System.Windows.Forms.Keys.Left: return CameraKeys.StrafeLeft;
  1152.                 case System.Windows.Forms.Keys.Right: return CameraKeys.StrafeRight;
  1153.                 case System.Windows.Forms.Keys.Up: return CameraKeys.MoveForward;
  1154.                 case System.Windows.Forms.Keys.Down: return CameraKeys.MoveBackward;
  1155.                 case System.Windows.Forms.Keys.Prior: return CameraKeys.MoveUp; // pgup
  1156.                 case System.Windows.Forms.Keys.Next: return CameraKeys.MoveDown; // pgdn
  1157.  
  1158.                 case System.Windows.Forms.Keys.A: return CameraKeys.StrafeLeft;
  1159.                 case System.Windows.Forms.Keys.D: return CameraKeys.StrafeRight;
  1160.                 case System.Windows.Forms.Keys.W: return CameraKeys.MoveForward;
  1161.                 case System.Windows.Forms.Keys.S: return CameraKeys.MoveBackward;
  1162.                 case System.Windows.Forms.Keys.Q: return CameraKeys.MoveUp; 
  1163.                 case System.Windows.Forms.Keys.E: return CameraKeys.MoveDown; 
  1164.  
  1165.                 case System.Windows.Forms.Keys.NumPad4: return CameraKeys.StrafeLeft;
  1166.                 case System.Windows.Forms.Keys.NumPad6: return CameraKeys.StrafeRight;
  1167.                 case System.Windows.Forms.Keys.NumPad8: return CameraKeys.MoveForward;
  1168.                 case System.Windows.Forms.Keys.NumPad2: return CameraKeys.MoveBackward;
  1169.                 case System.Windows.Forms.Keys.NumPad9: return CameraKeys.MoveUp; 
  1170.                 case System.Windows.Forms.Keys.NumPad3: return CameraKeys.MoveDown; 
  1171.  
  1172.                 case System.Windows.Forms.Keys.Home: return CameraKeys.Reset; 
  1173.             }
  1174.             // No idea
  1175.             return (CameraKeys)byte.MaxValue;
  1176.         }
  1177.  
  1178.  
  1179.         #region Instance Data
  1180.         protected Matrix viewMatrix; // View Matrix
  1181.         protected Matrix projMatrix; // Projection matrix
  1182.  
  1183.         protected System.Drawing.Point lastMousePosition;  // Last absolute position of mouse cursor
  1184.         protected bool isMouseLButtonDown;    // True if left button is down 
  1185.         protected bool isMouseMButtonDown;    // True if middle button is down 
  1186.         protected bool isMouseRButtonDown;    // True if right button is down 
  1187.         protected int currentButtonMask;   // mask of which buttons are down
  1188.         protected int mouseWheelDelta;     // Amount of middle wheel scroll (+/-) 
  1189.         protected Vector2 mouseDelta;          // Mouse relative delta smoothed over a few frames
  1190.         protected float framesToSmoothMouseData; // Number of frames to smooth mouse data over
  1191.  
  1192.         protected Vector3 defaultEye;          // Default camera eye position
  1193.         protected Vector3 defaultLookAt;       // Default LookAt position
  1194.         protected Vector3 eye;                 // Camera eye position
  1195.         protected Vector3 lookAt;              // LookAt position
  1196.         protected float cameraYawAngle;      // Yaw angle of camera
  1197.         protected float cameraPitchAngle;    // Pitch angle of camera
  1198.  
  1199.         protected System.Drawing.Rectangle dragRectangle; // Rectangle within which a drag can be initiated.
  1200.         protected Vector3 velocity;            // Velocity of camera
  1201.         protected bool isMovementDrag;        // If true, then camera movement will slow to a stop otherwise movement is instant
  1202.         protected Vector3 velocityDrag;        // Velocity drag force
  1203.         protected float dragTimer;           // Countdown timer to apply drag
  1204.         protected float totalDragTimeToZero; // Time it takes for velocity to go from full to 0
  1205.         protected Vector2 rotationVelocity;         // Velocity of camera
  1206.  
  1207.         protected float fieldOfView;                 // Field of view
  1208.         protected float aspectRatio;              // Aspect ratio
  1209.         protected float nearPlane;           // Near plane
  1210.         protected float farPlane;            // Far plane
  1211.  
  1212.         protected float rotationScaler;      // Scaler for rotation
  1213.         protected float moveScaler;          // Scaler for movement
  1214.  
  1215.         protected bool isInvertPitch;         // Invert the pitch axis
  1216.         protected bool isEnablePositionMovement; // If true, then the user can translate the camera/model 
  1217.         protected bool isEnableYAxisMovement; // If true, then camera can move in the y-axis
  1218.  
  1219.         protected bool isClipToBoundary;      // If true, then the camera will be clipped to the boundary
  1220.         protected Vector3 minBoundary;         // Min point in clip boundary
  1221.         protected Vector3 maxBoundary;         // Max point in clip boundary
  1222.  
  1223.         protected bool isResetCursorAfterMove;// If true, the class will reset the cursor position so that the cursor always has space to move 
  1224.  
  1225.         // State of the input
  1226.         protected bool[] keys;
  1227.         public static readonly Vector3 UpDirection = new Vector3(0,1,0);
  1228.         #endregion
  1229.  
  1230.         #region Simple Properties
  1231.         /// <summary>Is the camera being 'dragged' at all?</summary>
  1232.         public bool IsBeingDragged { get { return (isMouseLButtonDown || isMouseMButtonDown || isMouseRButtonDown); } }
  1233.         /// <summary>Is the left mouse button down</summary>
  1234.         public bool IsMouseLeftButtonDown { get { return isMouseLButtonDown; } }
  1235.         /// <summary>Is the right mouse button down</summary>
  1236.         public bool IsMouseRightButtonDown { get { return isMouseRButtonDown; } }
  1237.         /// <summary>Is the middle mouse button down</summary>
  1238.         public bool IsMouseMiddleButtonDown { get { return isMouseMButtonDown; } }
  1239.         /// <summary>Returns the view transformation matrix</summary>
  1240.         public Matrix ViewMatrix { get { return viewMatrix; } }
  1241.         /// <summary>Returns the projection transformation matrix</summary>
  1242.         public Matrix ProjectionMatrix { get { return projMatrix; } }
  1243.         /// <summary>Returns the location of the eye</summary>
  1244.         public Vector3 EyeLocation { get { return eye; } }
  1245.         /// <summary>Returns the look at point of the camera</summary>
  1246.         public Vector3 LookAtPoint { get { return lookAt; } }
  1247.         /// <summary>Is position movement enabled</summary>
  1248.         public bool IsPositionMovementEnabled { get {return isEnablePositionMovement; } set { isEnablePositionMovement = value; } }
  1249.         #endregion
  1250.         
  1251.         /// <summary>
  1252.         /// Abstract method to control camera during frame move
  1253.         /// </summary>
  1254.         public abstract void FrameMove(float elapsedTime);
  1255.  
  1256.         /// <summary>
  1257.         /// Constructor for the base camera class (Sets up camera defaults)
  1258.         /// </summary>
  1259.         protected Camera()
  1260.         {
  1261.             // Create the keys
  1262.             keys = new bool[(int)CameraKeys.MaxKeys];
  1263.  
  1264.             // Set attributes for the view matrix
  1265.             eye = Vector3.Empty;
  1266.             lookAt = new Vector3(0.0f, 0.0f, 1.0f);
  1267.  
  1268.             // Setup the view matrix
  1269.             SetViewParameters(eye, lookAt);
  1270.  
  1271.             // Setup the projection matrix
  1272.             SetProjectionParameters((float)Math.PI / 4, 1.0f, 1.0f, 1000.0f);
  1273.  
  1274.             // Store mouse information
  1275.             lastMousePosition = System.Windows.Forms.Cursor.Position;
  1276.             isMouseLButtonDown = false;
  1277.             isMouseRButtonDown = false;
  1278.             isMouseMButtonDown = false;
  1279.             mouseWheelDelta = 0;
  1280.             currentButtonMask = 0;
  1281.  
  1282.             // Setup camera rotations
  1283.             cameraYawAngle = 0.0f;
  1284.             cameraPitchAngle = 0.0f;
  1285.  
  1286.             dragRectangle = new System.Drawing.Rectangle(0, 0, int.MaxValue, int.MaxValue);
  1287.             velocity = Vector3.Empty;
  1288.             isMovementDrag = false;
  1289.             velocityDrag = Vector3.Empty;
  1290.             dragTimer = 0.0f;
  1291.             totalDragTimeToZero = 0.25f;
  1292.             rotationVelocity = Vector2.Empty;
  1293.             rotationScaler = 0.1f;
  1294.             moveScaler = 5.0f;
  1295.             isInvertPitch = false;
  1296.             isEnableYAxisMovement = true;
  1297.             isEnablePositionMovement = true;
  1298.             mouseDelta = Vector2.Empty;
  1299.             framesToSmoothMouseData = 2.0f;
  1300.             isClipToBoundary = false;
  1301.             minBoundary = new Vector3(-1.0f,-1.0f, -1.0f);
  1302.             maxBoundary = new Vector3(1,1,1);
  1303.             isResetCursorAfterMove = false;
  1304.         }
  1305.  
  1306.         /// <summary>
  1307.         /// Call this from your message proc so this class can handle window messages
  1308.         /// </summary>
  1309.         public virtual bool HandleMessages(IntPtr hWnd, NativeMethods.WindowMessage msg, IntPtr wParam, IntPtr lParam)
  1310.         {
  1311.             switch(msg)
  1312.             {
  1313.                 // Handle the keyboard
  1314.                 case NativeMethods.WindowMessage.KeyDown:
  1315.                     CameraKeys mappedKeyDown = MapKey(wParam);
  1316.                     if (mappedKeyDown != (CameraKeys)byte.MaxValue)
  1317.                     {
  1318.                         // Valid key was pressed, mark it as 'down'
  1319.                         keys[(int)mappedKeyDown] = true;
  1320.                     }
  1321.                     break;
  1322.                 case NativeMethods.WindowMessage.KeyUp:
  1323.                     CameraKeys mappedKeyUp = MapKey(wParam);
  1324.                     if (mappedKeyUp != (CameraKeys)byte.MaxValue)
  1325.                     {
  1326.                         // Valid key was let go, mark it as 'up'
  1327.                         keys[(int)mappedKeyUp] = false;
  1328.                     }
  1329.                     break;
  1330.  
  1331.                 // Handle the mouse
  1332.                 case NativeMethods.WindowMessage.LeftButtonDoubleClick:
  1333.                 case NativeMethods.WindowMessage.LeftButtonDown:
  1334.                 case NativeMethods.WindowMessage.RightButtonDoubleClick:
  1335.                 case NativeMethods.WindowMessage.RightButtonDown:
  1336.                 case NativeMethods.WindowMessage.MiddleButtonDoubleClick:
  1337.                 case NativeMethods.WindowMessage.MiddleButtonDown:
  1338.                 {
  1339.                     // Compute the drag rectangle in screen coord.
  1340.                     System.Drawing.Point cursor = new System.Drawing.Point(
  1341.                         NativeMethods.LoWord((uint)lParam.ToInt32()),
  1342.                         NativeMethods.HiWord((uint)lParam.ToInt32()));
  1343.  
  1344.                     // Update the variable state
  1345.                     if ( ((msg == NativeMethods.WindowMessage.LeftButtonDown) ||
  1346.                         (msg == NativeMethods.WindowMessage.LeftButtonDoubleClick) )
  1347.                         && dragRectangle.Contains(cursor) )
  1348.                     {
  1349.                         isMouseLButtonDown = true; currentButtonMask |= (int)MouseButtonMask.Left;
  1350.                     }
  1351.                     if ( ((msg == NativeMethods.WindowMessage.MiddleButtonDown) ||
  1352.                         (msg == NativeMethods.WindowMessage.MiddleButtonDoubleClick) )
  1353.                         && dragRectangle.Contains(cursor) )
  1354.                     {
  1355.                         isMouseMButtonDown = true; currentButtonMask |= (int)MouseButtonMask.Middle;
  1356.                     }
  1357.                     if ( ((msg == NativeMethods.WindowMessage.RightButtonDown) ||
  1358.                         (msg == NativeMethods.WindowMessage.RightButtonDoubleClick) )
  1359.                         && dragRectangle.Contains(cursor) )
  1360.                     {
  1361.                         isMouseRButtonDown = true; currentButtonMask |= (int)MouseButtonMask.Right;
  1362.                     }
  1363.  
  1364.                     // Capture the mouse, so if the mouse button is 
  1365.                     // released outside the window, we'll get the button up messages
  1366.                     NativeMethods.SetCapture(hWnd);
  1367.  
  1368.                     lastMousePosition = System.Windows.Forms.Cursor.Position;
  1369.                     return true;
  1370.                 }
  1371.                 case NativeMethods.WindowMessage.LeftButtonUp:
  1372.                 case NativeMethods.WindowMessage.RightButtonUp:
  1373.                 case NativeMethods.WindowMessage.MiddleButtonUp:
  1374.                 {
  1375.                     // Update member var state
  1376.                     if (msg == NativeMethods.WindowMessage.LeftButtonUp) { isMouseLButtonDown = false; currentButtonMask &= ~(int)MouseButtonMask.Left; }
  1377.                     if (msg == NativeMethods.WindowMessage.RightButtonUp) { isMouseRButtonDown = false; currentButtonMask &= ~(int)MouseButtonMask.Right; }
  1378.                     if (msg == NativeMethods.WindowMessage.MiddleButtonUp) { isMouseMButtonDown = false; currentButtonMask &= ~(int)MouseButtonMask.Middle; }
  1379.  
  1380.                     // Release the capture if no mouse buttons are down
  1381.                     if (!isMouseLButtonDown && !isMouseMButtonDown && !isMouseRButtonDown)
  1382.                     {
  1383.                         NativeMethods.ReleaseCapture();
  1384.                     }
  1385.                 }
  1386.                     break;
  1387.  
  1388.                 // Handle the mouse wheel
  1389.                 case NativeMethods.WindowMessage.MouseWheel:
  1390.                     mouseWheelDelta = NativeMethods.HiWord((uint)wParam.ToInt32()) / 120;
  1391.                     break;
  1392.             }
  1393.  
  1394.             return false;
  1395.         }
  1396.  
  1397.  
  1398.         /// <summary>
  1399.         /// Reset the camera's position back to the default
  1400.         /// </summary>
  1401.         public virtual void Reset()
  1402.         {
  1403.             SetViewParameters(defaultEye, defaultLookAt);
  1404.         }
  1405.  
  1406.         /// <summary>
  1407.         /// Client can call this to change the position and direction of camera
  1408.         /// </summary>
  1409.         public unsafe virtual void SetViewParameters(Vector3 eyePt, Vector3 lookAtPt)
  1410.         {
  1411.             // Store the data
  1412.             defaultEye = eye = eyePt;
  1413.             defaultLookAt = lookAt = lookAtPt;
  1414.  
  1415.             // Calculate the view matrix
  1416.             viewMatrix = Matrix.LookAtLH(eye, lookAt, UpDirection);
  1417.  
  1418.             // Get the inverted matrix
  1419.             Matrix inverseView = Matrix.Invert(viewMatrix);
  1420.  
  1421.             // The axis basis vectors and camera position are stored inside the 
  1422.             // position matrix in the 4 rows of the camera's world matrix.
  1423.             // To figure out the yaw/pitch of the camera, we just need the Z basis vector
  1424.             Vector3* pZBasis = (Vector3*)&inverseView.M31;
  1425.             cameraYawAngle = (float)Math.Atan2(pZBasis->X, pZBasis->Z);
  1426.             float len = (float)Math.Sqrt(pZBasis->Z * pZBasis->Z + pZBasis->X * pZBasis->X);
  1427.             cameraPitchAngle = -(float)Math.Atan2(pZBasis->Y, len);
  1428.         }
  1429.  
  1430.         /// <summary>
  1431.         /// Calculates the projection matrix based on input params
  1432.         /// </summary>
  1433.         public virtual void SetProjectionParameters(float fov, float aspect, float near, float far)
  1434.         {
  1435.             // Set attributes for the projection matrix
  1436.             fieldOfView = fov;
  1437.             aspectRatio = aspect;
  1438.             nearPlane = near;
  1439.             farPlane = far;
  1440.  
  1441.             projMatrix = Matrix.PerspectiveFovLH(fov, aspect, near, far);
  1442.         }
  1443.  
  1444.         /// <summary>
  1445.         /// Figure out the mouse delta based on mouse movement
  1446.         /// </summary>
  1447.         protected void UpdateMouseDelta(float elapsedTime)
  1448.         {
  1449.             // Get the current mouse position
  1450.             System.Drawing.Point current = System.Windows.Forms.Cursor.Position;
  1451.  
  1452.             // Calculate how far it's moved since the last frame
  1453.             System.Drawing.Point delta = new System.Drawing.Point(current.X - lastMousePosition.X,
  1454.                 current.Y - lastMousePosition.Y);
  1455.  
  1456.             // Record the current position for next time
  1457.             lastMousePosition = current;
  1458.  
  1459.             if (isResetCursorAfterMove)
  1460.             {
  1461.                 // Set position of camera to center of desktop, 
  1462.                 // so it always has room to move.  This is very useful
  1463.                 // if the cursor is hidden.  If this isn't done and cursor is hidden, 
  1464.                 // then invisible cursor will hit the edge of the screen 
  1465.                 // and the user can't tell what happened
  1466.                 System.Windows.Forms.Screen activeScreen = System.Windows.Forms.Screen.PrimaryScreen;
  1467.                 System.Drawing.Point center = new System.Drawing.Point(activeScreen.Bounds.Width / 2,
  1468.                     activeScreen.Bounds.Height / 2);
  1469.                 System.Windows.Forms.Cursor.Position = center;
  1470.                 lastMousePosition = center;
  1471.             }
  1472.  
  1473.             // Smooth the relative mouse data over a few frames so it isn't 
  1474.             // jerky when moving slowly at low frame rates.
  1475.             float percentOfNew = 1.0f / framesToSmoothMouseData;
  1476.             float percentOfOld = 1.0f - percentOfNew;
  1477.             mouseDelta.X = mouseDelta.X*percentOfNew + delta.X*percentOfNew;
  1478.             mouseDelta.Y = mouseDelta.Y*percentOfNew + delta.Y*percentOfNew;
  1479.  
  1480.             rotationVelocity = mouseDelta * rotationScaler;
  1481.         }
  1482.  
  1483.         /// <summary>
  1484.         /// Figure out the velocity based on keyboard input & drag if any
  1485.         /// </summary>
  1486.         protected void UpdateVelocity(float elapsedTime)
  1487.         {
  1488.             Vector3 accel = Vector3.Empty;
  1489.  
  1490.             if (isEnablePositionMovement)
  1491.             {
  1492.                 // Update acceleration vector based on keyboard state
  1493.                 if (keys[(int)CameraKeys.MoveForward])
  1494.                     accel.Z += 1.0f;
  1495.                 if (keys[(int)CameraKeys.MoveBackward])
  1496.                     accel.Z -= 1.0f;
  1497.                 if (isEnableYAxisMovement)
  1498.                 {
  1499.                     if (keys[(int)CameraKeys.MoveUp])
  1500.                         accel.Y += 1.0f;
  1501.                     if (keys[(int)CameraKeys.MoveDown])
  1502.                         accel.Y -= 1.0f;
  1503.                 }
  1504.                 if (keys[(int)CameraKeys.StrafeRight])
  1505.                     accel.X += 1.0f;
  1506.                 if (keys[(int)CameraKeys.StrafeLeft])
  1507.                     accel.X -= 1.0f;
  1508.             }
  1509.             // Normalize vector so if moving 2 dirs (left & forward), 
  1510.             // the camera doesn't move faster than if moving in 1 dir
  1511.             accel.Normalize();
  1512.             // Scale the acceleration vector
  1513.             accel *= moveScaler;
  1514.  
  1515.             if (isMovementDrag)
  1516.             {
  1517.                 // Is there any acceleration this frame?
  1518.                 if (accel.LengthSq() > 0)
  1519.                 {
  1520.                     // If so, then this means the user has pressed a movement key
  1521.                     // so change the velocity immediately to acceleration 
  1522.                     // upon keyboard input.  This isn't normal physics
  1523.                     // but it will give a quick response to keyboard input
  1524.                     velocity = accel;
  1525.                     dragTimer = totalDragTimeToZero;
  1526.                     velocityDrag = accel * (1 / dragTimer);
  1527.                 }
  1528.                 else
  1529.                 {
  1530.                     // If no key being pressed, then slowly decrease velocity to 0
  1531.                     if (dragTimer > 0)
  1532.                     {
  1533.                         velocity -= (velocityDrag * elapsedTime);
  1534.                         dragTimer -= elapsedTime;
  1535.                     }
  1536.                     else
  1537.                     {
  1538.                         // Zero velocity
  1539.                         velocity = Vector3.Empty;
  1540.                     }
  1541.                 }
  1542.             }
  1543.             else
  1544.             {
  1545.                 // No drag, so immediately change the velocity
  1546.                 velocity = accel;
  1547.             }
  1548.         }
  1549.  
  1550.         /// <summary>
  1551.         /// Clamps V to lie inside boundaries
  1552.         /// </summary>
  1553.         protected void ConstrainToBoundary(ref Vector3 v)
  1554.         {
  1555.             // Constrain vector to a bounding box 
  1556.             v.X = Math.Max(v.X, minBoundary.X);
  1557.             v.Y = Math.Max(v.Y, minBoundary.Y);
  1558.             v.Z = Math.Max(v.Z, minBoundary.Z);
  1559.  
  1560.             v.X = Math.Min(v.X, maxBoundary.X);
  1561.             v.Y = Math.Min(v.Y, maxBoundary.Y);
  1562.             v.Z = Math.Min(v.Z, maxBoundary.Z);
  1563.  
  1564.         }
  1565.     }
  1566.  
  1567.     /// <summary>
  1568.     /// Simple first person camera class that moves and rotates.
  1569.     /// It allows yaw and pitch but not roll.  It uses keyboard and 
  1570.     /// cursor to respond to keyboard and mouse input and updates the 
  1571.     /// view matrix based on input.  
  1572.     /// </summary>
  1573.     public class FirstPersonCamera : Camera
  1574.     {    
  1575.         // Mask to determine which button to enable for rotation
  1576.         protected int activeButtonMask = (int)(MouseButtonMask.Left | MouseButtonMask.Middle | MouseButtonMask.Right);
  1577.         // World matrix of the camera (inverse of the view matrix)
  1578.         protected Matrix cameraWorld;
  1579.  
  1580.         /// <summary>
  1581.         /// Update the view matrix based on user input & elapsed time
  1582.         /// </summary>
  1583.         public override void FrameMove(float elapsedTime)
  1584.         {
  1585.             // Reset the camera if necessary
  1586.             if (keys[(int)CameraKeys.Reset])
  1587.                 Reset();
  1588.  
  1589.             // Get the mouse movement (if any) if the mouse buttons are down
  1590.             if ((activeButtonMask & currentButtonMask) != 0)
  1591.                 UpdateMouseDelta(elapsedTime);
  1592.  
  1593.             // Get amount of velocity based on the keyboard input and drag (if any)
  1594.             UpdateVelocity(elapsedTime);
  1595.  
  1596.             // Simple euler method to calculate position delta
  1597.             Vector3 posDelta = velocity * elapsedTime;
  1598.  
  1599.             // If rotating the camera 
  1600.             if ((activeButtonMask & currentButtonMask) != 0)
  1601.             {
  1602.                 // Update the pitch & yaw angle based on mouse movement
  1603.                 float yawDelta   = rotationVelocity.X;
  1604.                 float pitchDelta = rotationVelocity.Y;
  1605.  
  1606.                 // Invert pitch if requested
  1607.                 if (isInvertPitch)
  1608.                     pitchDelta = -pitchDelta;
  1609.  
  1610.                 cameraPitchAngle += pitchDelta;
  1611.                 cameraYawAngle   += yawDelta;
  1612.  
  1613.                 // Limit pitch to straight up or straight down
  1614.                 cameraPitchAngle = Math.Max(-(float)Math.PI/2.0f,  cameraPitchAngle);
  1615.                 cameraPitchAngle = Math.Min(+(float)Math.PI/2.0f,  cameraPitchAngle);
  1616.             }
  1617.  
  1618.             // Make a rotation matrix based on the camera's yaw & pitch
  1619.             Matrix cameraRotation = Matrix.RotationYawPitchRoll(cameraYawAngle, cameraPitchAngle, 0 );
  1620.  
  1621.             // Transform vectors based on camera's rotation matrix
  1622.             Vector3 localUp = new Vector3(0,1,0);
  1623.             Vector3 localAhead = new Vector3(0,0,1);
  1624.             Vector3 worldUp = Vector3.TransformCoordinate(localUp, cameraRotation);
  1625.             Vector3 worldAhead = Vector3.TransformCoordinate(localAhead, cameraRotation);
  1626.  
  1627.             // Transform the position delta by the camera's rotation 
  1628.             Vector3 posDeltaWorld = Vector3.TransformCoordinate(posDelta, cameraRotation);
  1629.             if (!isEnableYAxisMovement)
  1630.                 posDeltaWorld.Y = 0.0f;
  1631.  
  1632.             // Move the eye position 
  1633.             eye += posDeltaWorld;
  1634.             if (isClipToBoundary)
  1635.                 ConstrainToBoundary(ref eye);
  1636.  
  1637.             // Update the lookAt position based on the eye position 
  1638.             lookAt = eye + worldAhead;
  1639.  
  1640.             // Update the view matrix
  1641.             viewMatrix = Matrix.LookAtLH(eye, lookAt, worldUp);
  1642.             cameraWorld = Matrix.Invert(viewMatrix);
  1643.         }
  1644.  
  1645.         /// <summary>
  1646.         /// Enable or disable each of the mouse buttons for rotation drag.
  1647.         /// </summary>
  1648.         public void SetRotationButtons(bool left, bool middle, bool right)
  1649.         {
  1650.             activeButtonMask = (left ? (int)MouseButtonMask.Left : 0) |
  1651.                 (middle ? (int)MouseButtonMask.Middle : 0) |
  1652.                 (right ? (int)MouseButtonMask.Right : 0);
  1653.         }
  1654.     }
  1655.  
  1656.     /// <summary>
  1657.     /// Simple model viewing camera class that rotates around the object.
  1658.     /// </summary>
  1659.     public class ModelViewerCamera : Camera
  1660.     {
  1661.         #region Instance Data
  1662.         protected ArcBall worldArcball = new ArcBall();
  1663.         protected ArcBall viewArcball = new ArcBall();
  1664.         protected Vector3 modelCenter;
  1665.         protected Matrix lastModelRotation; // Last arcball rotation matrix for model 
  1666.         protected Matrix lastCameraRotation; // Last rotation matrix for camera
  1667.         protected Matrix modelRotation; // Rotation matrix of model
  1668.         protected Matrix world; // World Matrix of model
  1669.  
  1670.         protected int rotateModelButtonMask;
  1671.         protected int zoomButtonMask;
  1672.         protected int rotateCameraButtonMask;
  1673.  
  1674.         protected bool isPitchLimited;
  1675.         protected float radius; // Distance from the camera to model 
  1676.         protected float defaultRadius; // Distance from the camera to model 
  1677.         protected float minRadius; // Min radius
  1678.         protected float maxRadius; // Max radius
  1679.         protected bool attachCameraToModel;
  1680.         #endregion 
  1681.  
  1682.         #region Simple Properties/Set Methods
  1683.         /// <summary>The minimum radius</summary>
  1684.         public float MinimumRadius { get { return minRadius; } set { minRadius = value; } }
  1685.         /// <summary>The maximum radius</summary>
  1686.         public float MaximumRadius { get { return maxRadius; } set { maxRadius = value; } }
  1687.         /// <summary>Gets the world matrix</summary>
  1688.         public Matrix WorldMatrix { get { return world; } }
  1689.         /// <summary>Sets the world quat</summary>
  1690.         public void SetWorldQuat(Quaternion q) { worldArcball.CurrentQuaternion = q; }
  1691.         /// <summary>Sets the view quat</summary>
  1692.         public void SetViewQuat(Quaternion q) { viewArcball.CurrentQuaternion = q; }
  1693.         /// <summary>Sets whether the pitch is limited or not</summary>
  1694.         public void SetIsPitchLimited(bool limit) { isPitchLimited = limit; }
  1695.         /// <summary>Sets the model's center</summary>
  1696.         public void SetModelCenter(Vector3 c) { modelCenter = c; }
  1697.         /// <summary>Sets radius</summary>
  1698.         public void SetRadius(float r, float min, float max) { radius = defaultRadius = r; minRadius = min; maxRadius = max;}
  1699.         /// <summary>Sets radius</summary>
  1700.         public void SetRadius(float r) { defaultRadius = r; minRadius = 1.0f; maxRadius = float.MaxValue;}
  1701.         /// <summary>Sets arcball window</summary>
  1702.         public void SetWindow(int w, int h, float r) { worldArcball.SetWindow(w,h,r); viewArcball.SetWindow(w,h,r); }
  1703.         /// <summary>Sets arcball window</summary>
  1704.         public void SetWindow(int w, int h) { worldArcball.SetWindow(w,h,0.9f); viewArcball.SetWindow(w,h,0.9f); }
  1705.         /// <summary>Sets button masks</summary>
  1706.         public void SetButtonMasks(int rotateModel, int zoom, int rotateCamera) { rotateCameraButtonMask = rotateCamera; zoomButtonMask = zoom; rotateModelButtonMask = rotateModel; }
  1707.         /// <summary>Is the camera attached to a model</summary>
  1708.         public bool IsAttachedToModel { get { return attachCameraToModel; } set { attachCameraToModel = value; } }
  1709.         #endregion
  1710.  
  1711.         /// <summary>
  1712.         /// Creates new instance of the model viewer camera
  1713.         /// </summary>
  1714.         public ModelViewerCamera()
  1715.         {
  1716.             world = Matrix.Identity;
  1717.             modelRotation = Matrix.Identity;
  1718.             lastModelRotation = Matrix.Identity;
  1719.             lastCameraRotation = Matrix.Identity;
  1720.             modelCenter = Vector3.Empty;
  1721.             radius = 5.0f;
  1722.             defaultRadius = 5.0f;
  1723.             minRadius = 1.0f;
  1724.             maxRadius = float.MaxValue;
  1725.             isPitchLimited = false;
  1726.             isEnablePositionMovement = false;
  1727.             attachCameraToModel = false;
  1728.  
  1729.             // Set button masks
  1730.             rotateModelButtonMask = (int)MouseButtonMask.Left;
  1731.             zoomButtonMask = (int)MouseButtonMask.Wheel;
  1732.             rotateCameraButtonMask = (int)MouseButtonMask.Right;
  1733.         }
  1734.  
  1735.         /// <summary>
  1736.         /// Update the view matrix based on user input & elapsed time
  1737.         /// </summary>
  1738.         public unsafe override void FrameMove(float elapsedTime)
  1739.         {
  1740.             // Reset the camera if necessary
  1741.             if (keys[(int)CameraKeys.Reset])
  1742.                 Reset();
  1743.  
  1744.             // Get the mouse movement (if any) if the mouse buttons are down
  1745.             if (currentButtonMask != 0)
  1746.                 UpdateMouseDelta(elapsedTime);
  1747.  
  1748.             // Get amount of velocity based on the keyboard input and drag (if any)
  1749.             UpdateVelocity(elapsedTime);
  1750.  
  1751.             // Simple euler method to calculate position delta
  1752.             Vector3 posDelta = velocity * elapsedTime;
  1753.  
  1754.             // Change the radius from the camera to the model based on wheel scrolling
  1755.             if ( (mouseWheelDelta != 0) && (zoomButtonMask == (int)MouseButtonMask.Wheel) )
  1756.                 radius -= mouseWheelDelta * radius * 0.1f;
  1757.             radius = Math.Min( maxRadius, radius );
  1758.             radius = Math.Max( minRadius, radius );
  1759.             mouseWheelDelta = 0;
  1760.  
  1761.             // Get the inverse of the arcball's rotation matrix
  1762.             Matrix cameraRotation = Matrix.Invert(viewArcball.RotationMatrix);
  1763.         
  1764.             // Transform vectors based on camera's rotation matrix
  1765.             Vector3 localUp = new Vector3(0,1,0);
  1766.             Vector3 localAhead = new Vector3(0,0,1);
  1767.             Vector3 worldUp = Vector3.TransformCoordinate(localUp, cameraRotation);
  1768.             Vector3 worldAhead = Vector3.TransformCoordinate(localAhead, cameraRotation);
  1769.  
  1770.             // Transform the position delta by the camera's rotation 
  1771.             Vector3 posDeltaWorld = Vector3.TransformCoordinate(posDelta, cameraRotation);
  1772.  
  1773.             // Move the lookAt position 
  1774.             lookAt += posDeltaWorld;
  1775.             if (isClipToBoundary)
  1776.                 ConstrainToBoundary(ref lookAt);
  1777.  
  1778.             // Update the eye point based on a radius away from the lookAt position
  1779.             eye = lookAt - worldAhead * radius;
  1780.  
  1781.             // Update the view matrix
  1782.             viewMatrix = Matrix.LookAtLH(eye, lookAt, worldUp);
  1783.             Matrix invView = Matrix.Invert(viewMatrix);
  1784.             invView.M41 = invView.M42 = invView.M43 = 0;
  1785.             Matrix modelLastRotInv = Matrix.Invert(lastModelRotation);
  1786.  
  1787.             // Accumulate the delta of the arcball's rotation in view space.
  1788.             // Note that per-frame delta rotations could be problematic over long periods of time.
  1789.             Matrix localModel = worldArcball.RotationMatrix;
  1790.             modelRotation *= viewMatrix * modelLastRotInv * localModel * invView;
  1791.             if ( viewArcball.IsBeingDragged && attachCameraToModel && !keys[(int)CameraKeys.ControlDown])
  1792.             {
  1793.                 // Attah camera to model by inverse of the model rotation
  1794.                 Matrix cameraRotInv = Matrix.Invert(lastCameraRotation);
  1795.                 Matrix delta = cameraRotInv * cameraRotation; // local to world matrix
  1796.                 modelRotation *= delta;
  1797.             }
  1798.             lastCameraRotation = cameraRotation;
  1799.             lastModelRotation = localModel;
  1800.  
  1801.             // Since we're accumulating delta rotations, we need to orthonormalize 
  1802.             // the matrix to prevent eventual matrix skew
  1803.             fixed(void* pxBasis = &modelRotation.M11)
  1804.             {
  1805.                 fixed(void* pyBasis = &modelRotation.M21)
  1806.                 {
  1807.                     fixed(void* pzBasis = &modelRotation.M31)
  1808.                     {
  1809.                         UnsafeNativeMethods.Vector3.Normalize((Vector3*)pxBasis, (Vector3*)pxBasis);
  1810.                         UnsafeNativeMethods.Vector3.Cross((Vector3*)pyBasis, (Vector3*)pzBasis, (Vector3*)pxBasis);
  1811.                         UnsafeNativeMethods.Vector3.Normalize((Vector3*)pyBasis, (Vector3*)pyBasis);
  1812.                         UnsafeNativeMethods.Vector3.Cross((Vector3*)pzBasis, (Vector3*)pxBasis, (Vector3*)pyBasis);
  1813.                     }
  1814.                 }
  1815.             }
  1816.  
  1817.             // Translate the rotation matrix to the same position as the lookAt position
  1818.             modelRotation.M41 = lookAt.X;
  1819.             modelRotation.M42 = lookAt.Y;
  1820.             modelRotation.M43 = lookAt.Z;
  1821.  
  1822.             // Translate world matrix so its at the center of the model
  1823.             Matrix trans = Matrix.Translation(-modelCenter.X, -modelCenter.Y, -modelCenter.Z);
  1824.             world = trans * modelRotation;
  1825.         }
  1826.  
  1827.         /// <summary>
  1828.         /// Reset the camera's position back to the default
  1829.         /// </summary>
  1830.         public override void Reset()
  1831.         {
  1832.             base.Reset();
  1833.             world = Matrix.Identity;
  1834.             modelRotation = Matrix.Identity;
  1835.             lastModelRotation = Matrix.Identity;
  1836.             lastCameraRotation = Matrix.Identity;
  1837.             radius = defaultRadius;
  1838.             worldArcball.Reset();
  1839.             viewArcball.Reset();
  1840.         }
  1841.  
  1842.         /// <summary>
  1843.         /// Override for setting the view parameters
  1844.         /// </summary>
  1845.         public override void SetViewParameters(Vector3 eyePt, Vector3 lookAtPt)
  1846.         {
  1847.             // Call base first
  1848.             base.SetViewParameters (eyePt, lookAtPt);
  1849.  
  1850.             // Propogate changes to the member arcball
  1851.             Matrix rotation = Matrix.LookAtLH(eyePt, lookAtPt, UpDirection);
  1852.             viewArcball.CurrentQuaternion = Quaternion.RotationMatrix(rotation);
  1853.  
  1854.             // Set the radius according to the distance
  1855.             Vector3 eyeToPoint = lookAtPt - eyePt;
  1856.             SetRadius(eyeToPoint.Length());
  1857.         }
  1858.  
  1859.         /// <summary>
  1860.         /// Call this from your message proc so this class can handle window messages
  1861.         /// </summary>
  1862.         public override bool HandleMessages(IntPtr hWnd, NativeMethods.WindowMessage msg, IntPtr wParam, IntPtr lParam)
  1863.         {
  1864.             // Call base first
  1865.             base.HandleMessages(hWnd, msg, wParam, lParam);
  1866.  
  1867.             if ( ( (msg == NativeMethods.WindowMessage.LeftButtonDown || msg == NativeMethods.WindowMessage.LeftButtonDoubleClick) && ((rotateModelButtonMask & (int)MouseButtonMask.Left) != 0 ) ) ||
  1868.                 ( (msg == NativeMethods.WindowMessage.RightButtonDown || msg == NativeMethods.WindowMessage.RightButtonDoubleClick) && ((rotateModelButtonMask & (int)MouseButtonMask.Right) != 0 ) ) ||
  1869.                 ( (msg == NativeMethods.WindowMessage.MiddleButtonDown || msg == NativeMethods.WindowMessage.MiddleButtonDoubleClick) && ((rotateModelButtonMask & (int)MouseButtonMask.Middle) != 0 ) ) )
  1870.             {
  1871.                 // Current mouse position
  1872.                 short mouseX = NativeMethods.LoWord((uint)lParam.ToInt32());
  1873.                 short mouseY = NativeMethods.HiWord((uint)lParam.ToInt32());
  1874.                 worldArcball.OnBegin(mouseX, mouseY);
  1875.             }
  1876.             if ( ( (msg == NativeMethods.WindowMessage.LeftButtonDown || msg == NativeMethods.WindowMessage.LeftButtonDoubleClick) && ((rotateCameraButtonMask & (int)MouseButtonMask.Left) != 0 ) ) ||
  1877.                 ( (msg == NativeMethods.WindowMessage.RightButtonDown || msg == NativeMethods.WindowMessage.RightButtonDoubleClick) && ((rotateCameraButtonMask & (int)MouseButtonMask.Right) != 0 ) ) ||
  1878.                 ( (msg == NativeMethods.WindowMessage.MiddleButtonDown || msg == NativeMethods.WindowMessage.MiddleButtonDoubleClick) && ((rotateCameraButtonMask & (int)MouseButtonMask.Middle) != 0 ) ) )
  1879.             {
  1880.                 // Current mouse position
  1881.                 short mouseX = NativeMethods.LoWord((uint)lParam.ToInt32());
  1882.                 short mouseY = NativeMethods.HiWord((uint)lParam.ToInt32());
  1883.                 viewArcball.OnBegin(mouseX, mouseY);
  1884.             }
  1885.             if (msg == NativeMethods.WindowMessage.MouseMove)
  1886.             {
  1887.                 // Current mouse position
  1888.                 short mouseX = NativeMethods.LoWord((uint)lParam.ToInt32());
  1889.                 short mouseY = NativeMethods.HiWord((uint)lParam.ToInt32());
  1890.                 worldArcball.OnMove(mouseX, mouseY);
  1891.                 viewArcball.OnMove(mouseX, mouseY);
  1892.             }
  1893.  
  1894.             if ( (msg == NativeMethods.WindowMessage.LeftButtonUp) && ((rotateModelButtonMask & (int)MouseButtonMask.Left) != 0 ) ||
  1895.                 (msg == NativeMethods.WindowMessage.RightButtonUp) && ((rotateModelButtonMask & (int)MouseButtonMask.Right) != 0 ) ||
  1896.                 (msg == NativeMethods.WindowMessage.MiddleButtonUp) && ((rotateModelButtonMask & (int)MouseButtonMask.Middle) != 0 ) )
  1897.             {
  1898.                 worldArcball.OnEnd();
  1899.             }
  1900.  
  1901.             if ( (msg == NativeMethods.WindowMessage.LeftButtonUp) && ((rotateCameraButtonMask & (int)MouseButtonMask.Left) != 0 ) ||
  1902.                 (msg == NativeMethods.WindowMessage.RightButtonUp) && ((rotateCameraButtonMask & (int)MouseButtonMask.Right) != 0 ) ||
  1903.                 (msg == NativeMethods.WindowMessage.MiddleButtonUp) && ((rotateCameraButtonMask & (int)MouseButtonMask.Middle) != 0 ) )
  1904.             {
  1905.                 viewArcball.OnEnd();
  1906.             }
  1907.  
  1908.             return false;
  1909.         }
  1910.     }
  1911.     #endregion
  1912.  
  1913.     #region Text Helper
  1914.     /// <summary>
  1915.     /// Manages the intertion point when drawing text
  1916.     /// </summary>
  1917.     public struct TextHelper
  1918.     {
  1919.         private Font textFont; // Used to draw the text
  1920.         private Sprite textSprite; // Used to cache the drawn text
  1921.         private int color; // Color to draw the text
  1922.         private System.Drawing.Point point; // Where to draw the text
  1923.         private int lineHeight; // Height of the lines
  1924.  
  1925.         /// <summary>
  1926.         /// Create a new instance of the text helper class
  1927.         /// </summary>
  1928.         public TextHelper(Font f, Sprite s, int l)
  1929.         {
  1930.             textFont = f;
  1931.             textSprite = s;
  1932.             lineHeight = l;
  1933.             color = unchecked((int)0xffffffff);
  1934.             point = System.Drawing.Point.Empty;
  1935.         }
  1936.  
  1937.         /// <summary>
  1938.         /// Draw a line of text
  1939.         /// </summary>
  1940.         public void DrawTextLine(string text)
  1941.         {
  1942.             if (textFont == null)
  1943.             {
  1944.                 throw new InvalidOperationException("You cannot draw text.  There is no font object.");
  1945.             }
  1946.             // Create the rectangle to draw to
  1947.             System.Drawing.Rectangle rect = new System.Drawing.Rectangle(point, System.Drawing.Size.Empty);
  1948.             textFont.DrawText(textSprite, text, rect, DrawTextFormat.NoClip, color);
  1949.  
  1950.             // Increase the line height
  1951.             point.Y += lineHeight;
  1952.         }
  1953.  
  1954.         /// <summary>
  1955.         /// Draw a line of text
  1956.         /// </summary>
  1957.         public void DrawTextLine(string text, params object[] args)
  1958.         {
  1959.             // Simply format the string and pass it on
  1960.             DrawTextLine(string.Format(text, args));
  1961.         }
  1962.  
  1963.         /// <summary>
  1964.         /// Insertion point of the text
  1965.         /// </summary>
  1966.         public void SetInsertionPoint(System.Drawing.Point p) { point = p; }
  1967.         public void SetInsertionPoint(int x, int y) { point.X = x; point.Y = y; }
  1968.  
  1969.         /// <summary>
  1970.         /// The color of the text
  1971.         /// </summary>
  1972.         public void SetForegroundColor(int c) { color = c; }
  1973.         public void SetForegroundColor(System.Drawing.Color c) { color = c.ToArgb(); }
  1974.  
  1975.         /// <summary>
  1976.         /// Begin the sprite rendering
  1977.         /// </summary>
  1978.         public void Begin()
  1979.         {
  1980.             if (textSprite != null)
  1981.             {
  1982.                 textSprite.Begin(SpriteFlags.AlphaBlend | SpriteFlags.SortTexture);
  1983.             }
  1984.         }
  1985.  
  1986.         /// <summary>
  1987.         /// End the sprite
  1988.         /// </summary>
  1989.         public void End()
  1990.         {
  1991.             if (textSprite != null)
  1992.             {
  1993.                 textSprite.End();
  1994.             }
  1995.         }
  1996.     }
  1997.         #endregion
  1998.  
  1999.     #region Utility
  2000.         /// <summary>
  2001.         /// Misc utility functionality
  2002.         /// </summary>
  2003.         public class Utility
  2004.         {
  2005.             // Constants for SDK Path registry keys
  2006.             private const string sdkPath = "Software\\Microsoft\\DirectX SDK";
  2007.             private const string sdkKey = "DX9O4SDK Samples Path";
  2008.  
  2009.             // Constants for search folders
  2010.             private const string CurrentFolder = @".\";
  2011.             private const string MediaPath = @"Media\";
  2012.  
  2013.             // Typical folder locations
  2014.             //      .\
  2015.             //      ..\
  2016.             //      ..\..\
  2017.             //      %EXE_DIR%\
  2018.             //      %EXE_DIR%\..\
  2019.             //      %EXE_DIR%\..\..\
  2020.             //      %EXE_DIR%\..\%EXE_NAME%
  2021.             //      %EXE_DIR%\..\..\%EXE_NAME%
  2022.             //      DXSDK media path
  2023.             private static readonly string[] TypicalFolders = new string[] { CurrentFolder, @"..\",
  2024.                                                                                @"..\..\", @"{0}\", @"{0}\..\", @"{0}\..\..\", @"{0}\..\{1}\", @"{0}\..\..\{1}\" };
  2025.             private Utility() { /* Private Constructor */ }
  2026.  
  2027.             /// <summary>
  2028.             /// Returns the DirectX SDK media path
  2029.             /// </summary>
  2030.             public static string SdkMediaPath
  2031.             {
  2032.                 get
  2033.                 {
  2034.                     using(Microsoft.Win32.RegistryKey rKey = Microsoft.Win32.Registry.LocalMachine.OpenSubKey(sdkPath))
  2035.                     {
  2036.                         string sReg = string.Empty;
  2037.                         if (rKey != null)
  2038.                         {
  2039.                             sReg = rKey.GetValue(sdkKey) as string;
  2040.                         }
  2041.  
  2042.                         if (sReg != null) 
  2043.                         {
  2044.                             return (sReg + @"\Media\");
  2045.                         }
  2046.                         else 
  2047.                         {
  2048.                             return string.Empty;
  2049.                         }
  2050.                     }
  2051.                 }
  2052.             }
  2053.             /// <summary>
  2054.             /// Returns a valid path to a DXSDK media file
  2055.             /// </summary>
  2056.             /// <param name="path">Initial path to search</param>
  2057.             /// <param name="filename">Filename we're searching for</param>
  2058.             /// <returns>Full path to the file</returns>
  2059.             public static string FindMediaFile(string filename)
  2060.             {
  2061.                 // Find out the executing assembly information
  2062.                 System.Reflection.Assembly executingAssembly = System.Reflection.Assembly.GetExecutingAssembly();
  2063.                 // Now check the typical folders, before you can do that you'll need to get 
  2064.                 // the executable name
  2065.                 string exeName = System.IO.Path.GetFileNameWithoutExtension(executingAssembly.Location);
  2066.                 // And the executable folder
  2067.                 string exeFolder = System.IO.Path.GetDirectoryName(executingAssembly.Location);
  2068.  
  2069.                 string filePath;
  2070.                 // Now you can search the typical folders
  2071.                 if (SearchTypicalFolders(filename, exeFolder, exeName, out filePath))
  2072.                 {
  2073.                     return filePath;
  2074.                 }
  2075.  
  2076.                 // The file wasn't found again, search the folders with \media on them
  2077.                 // Now you can search the typical folders
  2078.                 if (SearchTypicalFolders(filename + MediaPath, exeFolder, exeName, out filePath))
  2079.                 {
  2080.                     return filePath;
  2081.                 }
  2082.  
  2083.                 // We still haven't found the file yet, we should search the parents folders now
  2084.                 if (SearchParentFolders(filename, CurrentFolder, "", out filePath))
  2085.                 {
  2086.                     return filePath;
  2087.                 }
  2088.                 // We still haven't found the file yet, now search from the exe folder
  2089.                 if (SearchParentFolders(filename, exeFolder, exeName, out filePath))
  2090.                 {
  2091.                     return filePath;
  2092.                 }
  2093.  
  2094.                 // We still haven't found the file yet, we should search the parents folders now, but append media
  2095.                 if (SearchParentFolders(filename, CurrentFolder, MediaPath, out filePath))
  2096.                 {
  2097.                     return filePath;
  2098.                 }
  2099.                 // We still haven't found the file yet, now search from the exe folder and append media
  2100.                 if (SearchParentFolders(filename, exeFolder, AppendDirectorySeparator(exeName) + MediaPath, out filePath))
  2101.                 {
  2102.                     return filePath;
  2103.                 }
  2104.  
  2105.  
  2106.                 // We still haven't found the file yet, the built samples are prefixed with 'cs', so see if that's the case
  2107.                 if (exeName.ToLower().StartsWith("cs"))
  2108.                 {
  2109.                     // Build the new exe name by stripping off the 'cs' prefix and doing the searches again
  2110.                     string newExeName = exeName.Substring(2, exeName.Length - 2);
  2111.                     if (SearchParentFolders(filename, exeFolder, newExeName, out filePath))
  2112.                     {
  2113.                         return filePath;
  2114.                     }
  2115.                     // We still haven't found the file yet, now search from the exe folder and append media
  2116.                     if (SearchParentFolders(filename, exeFolder, AppendDirectorySeparator(newExeName) + MediaPath, out filePath))
  2117.                     {
  2118.                         return filePath;
  2119.                     }
  2120.                 }
  2121.                 throw new MediaNotFoundException();
  2122.             }
  2123.  
  2124.             /// <summary>
  2125.             /// Will search the typical list of folders for the file first
  2126.             /// </summary>
  2127.             /// <param name="filename">File we are looking for</param>
  2128.             /// <param name="exeFolder">Folder of the executable</param>
  2129.             /// <param name="exeName">Name of the executable</param>
  2130.             /// <param name="fullPath">Returned path if file is found.</param>
  2131.             /// <returns>true if the file was found; false otherwise</returns>
  2132.             private static bool SearchTypicalFolders(string filename, string exeFolder, string exeName, out string fullPath)
  2133.             {
  2134.                 // First scan through each typical folder and see if we found the file
  2135.                 for (int i = 0; i < TypicalFolders.Length; i++)
  2136.                 {
  2137.                     try
  2138.                     {
  2139.                         FileInfo info = new FileInfo(string.Format(TypicalFolders[i], exeFolder, exeName) + filename);
  2140.                         if (info.Exists)
  2141.                         {
  2142.                             fullPath = info.FullName;
  2143.                             return true;
  2144.                         }
  2145.                     }
  2146.                     catch(NotSupportedException)
  2147.                     {
  2148.                         // This exception will be fired if the filename is not supported
  2149.                         continue;
  2150.                     }
  2151.                 }
  2152.  
  2153.                 // Wasn't in a typical folder, search the SDK folder
  2154.                 string sdkPath = SdkMediaPath + filename;
  2155.                 if (File.Exists(sdkPath))
  2156.                 {
  2157.                     fullPath = sdkPath;
  2158.                     return true;
  2159.                 }
  2160.                 // We never found any of the files
  2161.                 fullPath = string.Empty;
  2162.                 return false;
  2163.             }
  2164.  
  2165.             /// <summary>
  2166.             /// Will search the parents of files looking for media
  2167.             /// </summary>
  2168.             /// <param name="filename">File we are looking for</param>
  2169.             /// <param name="rootNode">Folder of the executable</param>
  2170.             /// <param name="leafName">Name of the executable</param>
  2171.             /// <param name="fullPath">Returned path if file is found.</param>
  2172.             /// <returns>true if the file was found; false otherwise</returns>
  2173.             private static bool SearchParentFolders(string filename, string rootNode, string leafName, out string fullPath)
  2174.             {
  2175.                 // Set the out parameter first
  2176.                 fullPath = string.Empty;
  2177.                 try
  2178.                 {
  2179.                     // Search the root node first
  2180.                     FileInfo info = new FileInfo( AppendDirectorySeparator(rootNode) + AppendDirectorySeparator(leafName) + filename);
  2181.                     if (info.Exists)
  2182.                     {
  2183.                         fullPath = info.FullName;
  2184.                         return true;
  2185.                     }
  2186.                 }
  2187.                 catch(NotSupportedException)
  2188.                 {
  2189.                     // The arguments passed in are not supported, fail now
  2190.                     return false;
  2191.                 }
  2192.  
  2193.                 // Are we in the root yet?
  2194.                 DirectoryInfo dir = new DirectoryInfo(rootNode);
  2195.                 if (dir.Parent != null)
  2196.                 {
  2197.                     return SearchParentFolders(filename, dir.Parent.FullName, leafName, out fullPath);
  2198.                 }
  2199.                 else
  2200.                 {
  2201.                     // We never found any of the files
  2202.                     return false;
  2203.                 }
  2204.             }
  2205.  
  2206.             /// <summary>
  2207.             /// Returns a valid string with a directory separator at the end.
  2208.             /// </summary>
  2209.             public static string AppendDirectorySeparator(string pathName)
  2210.             {
  2211.                 if (!pathName.EndsWith(@"\"))
  2212.                     return pathName + @"\";
  2213.  
  2214.                 return pathName;
  2215.             }
  2216.  
  2217.             /// <summary>Returns the view matrix for a cube map face</summary>
  2218.             public static Matrix GetCubeMapViewMatrix(CubeMapFace face)
  2219.             {
  2220.                 Vector3 vEyePt = new Vector3(0.0f, 0.0f, 0.0f);
  2221.                 Vector3 vLookDir = new Vector3();
  2222.                 Vector3 vUpDir = new Vector3();
  2223.  
  2224.                 switch (face)
  2225.                 {
  2226.                     case CubeMapFace.PositiveX:
  2227.                         vLookDir = new Vector3(1.0f, 0.0f, 0.0f);
  2228.                         vUpDir   = new Vector3(0.0f, 1.0f, 0.0f);
  2229.                         break;
  2230.                     case CubeMapFace.NegativeX:
  2231.                         vLookDir = new Vector3(-1.0f, 0.0f, 0.0f);
  2232.                         vUpDir   = new Vector3(0.0f, 1.0f, 0.0f);
  2233.                         break;
  2234.                     case CubeMapFace.PositiveY:
  2235.                         vLookDir = new Vector3(0.0f, 1.0f, 0.0f);
  2236.                         vUpDir   = new Vector3(0.0f, 0.0f,-1.0f);
  2237.                         break;
  2238.                     case CubeMapFace.NegativeY:
  2239.                         vLookDir = new Vector3(0.0f,-1.0f, 0.0f);
  2240.                         vUpDir   = new Vector3(0.0f, 0.0f, 1.0f);
  2241.                         break;
  2242.                     case CubeMapFace.PositiveZ:
  2243.                         vLookDir = new Vector3(0.0f, 0.0f, 1.0f);
  2244.                         vUpDir   = new Vector3(0.0f, 1.0f, 0.0f);
  2245.                         break;
  2246.                     case CubeMapFace.NegativeZ:
  2247.                         vLookDir = new Vector3(0.0f, 0.0f,-1.0f);
  2248.                         vUpDir   = new Vector3(0.0f, 1.0f, 0.0f);
  2249.                         break;
  2250.                 }
  2251.  
  2252.                 // Set the view transform for this cubemap surface
  2253.                 Matrix matView = Matrix.LookAtLH(vEyePt, vLookDir, vUpDir);
  2254.                 return matView;
  2255.             }
  2256.             /// <summary>Returns the view matrix for a cube map face</summary>
  2257.             public static Matrix GetCubeMapViewMatrix(int face) { return GetCubeMapViewMatrix((CubeMapFace)face); }
  2258.         }
  2259.     #endregion
  2260.  
  2261.     #region Widgets
  2262.  
  2263.     #region Direction Widget
  2264.     /// <summary>Widget for controlling direction</summary>
  2265.     public class DirectionWidget
  2266.     {
  2267.         #region Class level data (Instance/Static)
  2268.         // Instance members
  2269.         private float widgetRadius = 1.0f;
  2270.         private ArcBall arc = new ArcBall();
  2271.         private Vector3 defaultDir = new Vector3(0,1,0);
  2272.         private Vector3 currentDir = new Vector3(0,1,0);
  2273.         private Matrix viewMatrix = Matrix.Identity;
  2274.         private Matrix rotation = Matrix.Identity;
  2275.         private Matrix rotationSnapshot = Matrix.Identity;
  2276.         private MouseButtonMask rotateMask = MouseButtonMask.Right;
  2277.  
  2278.         // Static members
  2279.         private static Device device = null;
  2280.         private static Effect effect = null;
  2281.         private static Mesh mesh = null;
  2282.         #endregion
  2283.  
  2284.         #region Properties
  2285.         /// <summary>Radius of this widget</summary>
  2286.         public float Radius { get { return widgetRadius; } set { widgetRadius = value; } }
  2287.         /// <summary>Light direction of this widget</summary>
  2288.         public Vector3 LightDirection { get { return currentDir; } set { currentDir = defaultDir = value; } }
  2289.         /// <summary>Is this widget being dragged</summary>
  2290.         public bool IsBeingDragged{ get { return arc.IsBeingDragged; } }
  2291.         /// <summary>Rotation button mask</summary>
  2292.         public MouseButtonMask RotateButtonMask{ get { return rotateMask; } set { rotateMask = value; }}
  2293.         #endregion
  2294.  
  2295.         #region Device handlers
  2296.         /// <summary>Called when the device has been created</summary>
  2297.         public static void OnCreateDevice(Device device)
  2298.         {
  2299.             // Store the device
  2300.             DirectionWidget.device = device;
  2301.  
  2302.             // Read the effect file
  2303.             string path = Utility.FindMediaFile("UI\\DXUTShared.fx");
  2304.  
  2305.             // If this fails, there should be debug output as to 
  2306.             // why the .fx file failed to compile (assuming you have dbmon running).
  2307.             // If you do not, you can turn on unmanaged debugging for this project.
  2308.             effect = Effect.FromFile(device, path, null, ShaderFlags.None, null);
  2309.  
  2310.             // Load the mesh with D3DX and get back a Mesh.  For this
  2311.             // sample we'll ignore the X file's embedded materials since we know 
  2312.             // exactly the model we're loading.  See the mesh samples such as
  2313.             // "OptimizedMesh" for a more generic mesh loading example.
  2314.             path = Utility.FindMediaFile("UI\\arrow.x");
  2315.             mesh = Mesh.FromFile(path, MeshFlags.Managed, device);
  2316.  
  2317.             // Optimize the mesh for this graphics card's vertex cache 
  2318.             // so when rendering the mesh's triangle list the vertices will 
  2319.             // cache hit more often so it won't have to re-execute the vertex shader 
  2320.             // on those vertices so it will improve perf.     
  2321.             int[] adj = mesh.ConvertPointRepsToAdjacency(null as GraphicsStream);
  2322.             mesh.OptimizeInPlace(MeshFlags.OptimizeVertexCache, adj);
  2323.         }
  2324.  
  2325.         /// <summary>Called when the device has been reset</summary>
  2326.         public void OnResetDevice(SurfaceDescription desc)
  2327.         {
  2328.             arc.SetWindow(desc.Width, desc.Height);
  2329.         }
  2330.         
  2331.         /// <summary>Called when the device has been lost</summary>
  2332.         public static void OnLostDevice()
  2333.         {
  2334.             if (effect != null)
  2335.                 effect.OnLostDevice();
  2336.         }
  2337.         
  2338.         /// <summary>Called when the device has been destroyed</summary>
  2339.         public static void OnDestroyDevice()
  2340.         {
  2341.             if (effect != null)
  2342.                 effect.Dispose();
  2343.             if (mesh != null)
  2344.                 mesh.Dispose();
  2345.             effect = null;
  2346.             mesh = null;
  2347.         }
  2348.         #endregion
  2349.         
  2350.         /// <summary>Handle messages from the window</summary>
  2351.         public bool HandleMessages(IntPtr hWnd, NativeMethods.WindowMessage msg, IntPtr wParam, IntPtr lParam)
  2352.         {
  2353.             // Current mouse position
  2354.             short mouseX = NativeMethods.LoWord((uint)lParam.ToInt32());
  2355.             short mouseY = NativeMethods.HiWord((uint)lParam.ToInt32());
  2356.  
  2357.             switch(msg)
  2358.             {
  2359.                 case NativeMethods.WindowMessage.LeftButtonDown:
  2360.                 case NativeMethods.WindowMessage.MiddleButtonDown:
  2361.                 case NativeMethods.WindowMessage.RightButtonDown:
  2362.                 {
  2363.                     if ( ((rotateMask & MouseButtonMask.Left) == MouseButtonMask.Left && msg == NativeMethods.WindowMessage.LeftButtonDown) ||
  2364.                         ((rotateMask & MouseButtonMask.Right) == MouseButtonMask.Right && msg == NativeMethods.WindowMessage.RightButtonDown) ||
  2365.                         ((rotateMask & MouseButtonMask.Middle) == MouseButtonMask.Middle && msg == NativeMethods.WindowMessage.MiddleButtonDown) )
  2366.                     {
  2367.                         arc.OnBegin(mouseX, mouseY);
  2368.                         NativeMethods.SetCapture(hWnd);
  2369.                     }
  2370.                     return true;
  2371.                 }
  2372.                 case NativeMethods.WindowMessage.MouseMove:
  2373.                 {
  2374.                     if (arc.IsBeingDragged)
  2375.                     {
  2376.                         arc.OnMove(mouseX, mouseY);
  2377.                         UpdateLightDirection();
  2378.                     }
  2379.                     return true;
  2380.                 }
  2381.                 case NativeMethods.WindowMessage.LeftButtonUp:
  2382.                 case NativeMethods.WindowMessage.RightButtonUp:
  2383.                 case NativeMethods.WindowMessage.MiddleButtonUp:
  2384.                 {
  2385.                     if ( ((rotateMask & MouseButtonMask.Left) == MouseButtonMask.Left && msg == NativeMethods.WindowMessage.LeftButtonUp) ||
  2386.                         ((rotateMask & MouseButtonMask.Right) == MouseButtonMask.Right && msg == NativeMethods.WindowMessage.RightButtonUp) ||
  2387.                         ((rotateMask & MouseButtonMask.Middle) == MouseButtonMask.Middle && msg == NativeMethods.WindowMessage.MiddleButtonUp) )
  2388.                     {
  2389.                         arc.OnEnd();
  2390.                         NativeMethods.ReleaseCapture();
  2391.                     }
  2392.                     
  2393.                     UpdateLightDirection();
  2394.                     return true;
  2395.                 }
  2396.             }
  2397.  
  2398.             // Didn't handle the message
  2399.             return false;
  2400.         }
  2401.  
  2402.         /// <summary>Updates the light direction</summary>
  2403.         private unsafe void UpdateLightDirection()
  2404.         {
  2405.             Matrix invView = Matrix.Invert(viewMatrix);
  2406.             invView.M41 = invView.M42 = invView.M43 = 0;
  2407.  
  2408.             Matrix lastRotationInv = Matrix.Invert(rotationSnapshot);
  2409.             Matrix rot = arc.RotationMatrix;
  2410.             rotationSnapshot = rot;
  2411.  
  2412.             // Accumulate the delta of the arcball's rotation in view space.
  2413.             // Note that per-frame delta rotations could be problematic over long periods of time.
  2414.             rotation *= (viewMatrix * lastRotationInv * rot * invView);
  2415.  
  2416.             // Since we're accumulating delta rotations, we need to orthonormalize 
  2417.             // the matrix to prevent eventual matrix skew
  2418.             fixed(void* pxBasis = &rotation.M11)
  2419.             {
  2420.                 fixed(void* pyBasis = &rotation.M21)
  2421.                 {
  2422.                     fixed(void* pzBasis = &rotation.M31)
  2423.                     {
  2424.                         UnsafeNativeMethods.Vector3.Normalize((Vector3*)pxBasis, (Vector3*)pxBasis);
  2425.                         UnsafeNativeMethods.Vector3.Cross((Vector3*)pyBasis, (Vector3*)pzBasis, (Vector3*)pxBasis);
  2426.                         UnsafeNativeMethods.Vector3.Normalize((Vector3*)pyBasis, (Vector3*)pyBasis);
  2427.                         UnsafeNativeMethods.Vector3.Cross((Vector3*)pzBasis, (Vector3*)pxBasis, (Vector3*)pyBasis);
  2428.                     }
  2429.                 }
  2430.             }
  2431.      
  2432.             // Transform the default direction vector by the light's rotation matrix
  2433.             currentDir = Vector3.TransformNormal(defaultDir, rotation);
  2434.         }
  2435.  
  2436.         /// <summary>Render the light widget</summary>
  2437.         public unsafe void OnRender(ColorValue color, Matrix view, Matrix proj, Vector3 eye)
  2438.         {
  2439.             // Store the view matrix
  2440.             viewMatrix = view;
  2441.  
  2442.             // Render the light arrows so the user can visually see the light direction
  2443.             effect.Technique = "RenderWith1LightNoTexture";
  2444.             effect.SetValue("g_MaterialDiffuseColor", color);
  2445.             Vector3 eyePt = Vector3.Normalize(eye);
  2446.  
  2447.             // Set the light direction value
  2448.             effect.SetValue("g_LightDir", &eyePt, sizeof(Vector3));
  2449.             
  2450.             // Rotate arrow model to point towards origin
  2451.             Vector3 at = Vector3.Empty;
  2452.             Vector3 up = new Vector3(0,1,0);
  2453.             Matrix rotateB = Matrix.RotationX((float)Math.PI);
  2454.             Matrix rotateA = Matrix.LookAtLH(currentDir, at, up);
  2455.             rotateA.Invert();
  2456.             Matrix rotate = rotateB * rotateA;
  2457.             Vector3 l = currentDir * widgetRadius * 1.0f;
  2458.             Matrix trans = Matrix.Translation(l);
  2459.             Matrix scale = Matrix.Scaling(widgetRadius * 0.2f, widgetRadius * 0.2f, widgetRadius * 0.2f);
  2460.  
  2461.             Matrix world = rotate * scale * trans;
  2462.             Matrix worldViewProj = world * viewMatrix * proj;
  2463.  
  2464.             effect.SetValue("g_mWorldViewProjection", worldViewProj);
  2465.             effect.SetValue("g_mWorld", world);
  2466.  
  2467.             // Render the arrows
  2468.             for (int subset = 0; subset < 2; subset++)
  2469.             {
  2470.                 int passes = effect.Begin(0);
  2471.                 for (int pass = 0; pass < passes; pass++)
  2472.                 {
  2473.                     effect.BeginPass(pass);
  2474.                     mesh.DrawSubset(subset);
  2475.                     effect.EndPass();
  2476.                 }
  2477.                 effect.End();
  2478.             }
  2479.  
  2480.         }
  2481.     }
  2482.     #endregion
  2483.  
  2484.     #endregion
  2485. }